We just learned how to create and test simple Parrot projects. The next step is to reexamine the star catalog handler we’ve been writing, and turn it into a testable project. There will be some changes in how this code gets its work done, but don’t expect any new features.
I know - you really want to start adding features and working on cool new stuff. So do I. My workspace is scattered with half-completed steps that talked about adding new things and using new Parrot features. They kept breaking, though. They kept breaking because I wasn’t building from a stable, testable foundation. Today we’re going to get that step our of the way.
Set up the project
Let’s apply what we learned last time putting together larger projects, and set up a project named
$ mkdir stellar $ mkdir stellar/t $ mkdir stellar/lib
setup.pir file is copied directly from the previous project.
I’m not ready for anything more elaborate.
The behavior will be defined in
lib/stellar.pir, which is initially empty.
We will steadily build up all of our functionality in the Stellar library and eventually add a very simple file to act as the face of Stellar for Parrot.
Right now, we just have an empty
The basic skeleton is in place. Now we can start adding the features we had written before.
It’s important to keep test files organized. One helpful approach is to think of each test as a story. This story describes a single specific thing we want the Stellar library to accomplish. All of the stories together provide a description of everything that users should be able to get from the library.
I like to start with the smallest useful test story I can. For this code, I think that would be extracting the details about a single entry in the HYG catalog. The whole catalog isn’t even needed. We could get away with using the header line and the line containing a star’s details.
Names for test files usually follow a common pattern. They start with a number and summarize what feature is being tested. What purpose does the number serve? Well, they are probably executed in the order that Parrot finds them, so that numbering provides a clue for test order. There are no promises about the order, though. The system looks at each story individually, and you should too. I like to think of the numbering as simply presenting the order that I came up with the stories. It provides a simple history of sorts. First I came up with that test, and I wrote this test after I was comfortable with the first.
In that spirit, I will name the first test story
Yes, I know that there is a typo in this test code.
Proper Name should be
We’ll come back to that.
What happens if this test is run while
stellar.pir is still empty?
It fails, of course.
It might be useful to look at how it fails.
stellar $ parrot setup.pir test t/01-extract-details.t .. Dubious, test returned 1 Failed 1/1 subtests Test Summary Report ------------------- t/01-extract-details.t (Tests: 0 Failed: 0) XXX Files=1, Tests=0, 0.014 wallclock secs Result: FAIL test fails current instr.: 'setup' pc 883 (runtime/parrot/library/distutils.pir:376) called from Sub 'main' pc 29 (setup.pir:17)
setup told us something we already know:
extract_star_details hasn’t been written yet.
This is different from a regular test failure, because Parrot couldn’t even get to the tests.
I’m showing this so that you recognize what’s going on when you see errors like this in your own library.
extract_star_details is easy enough.
Just copy the code from the earlier step.
Let’s run the test again.
$ parrot setup.pir test t/01-extract-details.t .. Failed 1/1 subtests Test Summary Report ------------------- t/01-extract-details.t (Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0.020 wallclock secs Result: FAIL test fails current instr.: 'setup' pc 883 (runtime/parrot/library/distutils.pir:376) called from Sub 'main' pc 29 (setup.pir:17)
There’s only one assertion, so we already know which one failed. This output is a little vague for larger test stories, though. Let’s run the test file directly.
$ parrot t/01-extract-details.t 1..1 not ok 1 - ProperName should be Sol # Have: # Want: Sol
Okay, now we can fix the typo.
The test should pass now that the correction has been made.
$ parrot setup.pir test t/01-extract-details.t .. ok All tests successful. Files=1, Tests=1, 0.014 wallclock secs Result: PASS
Don’t worry. I won’t submit you to this for every test run. It’s just important to know what failure looks like before we can reach success.
What have we accomplished so far? We now have a story in which the user, armed with a header line and a line describing a star, gets an object that she can examine and manipulate for her own purposes. If we wanted to be thorough, we could test every field. I’m not going to do that, though. One thing you want to avoid when making test stories is predicting the future. It’s easy to get distracted by testing every possible aspect of a single chunk of code when you could be working on the next story.
These stories aren’t static. We will come back and add more when some detail doesn’t work out the way we expect it to.
Our first story is pretty much out of the way. Let’s move on to the next one.
Next up is the string representation of a star.
In the original application, we had the
say_star_details sub, which printed the star information as soon as it had been prepared.
Stellar is more of a library, though.
This means that we can’t be completely sure what folks will want to do with the star summary once they have it.
They might want to print it, but they might also want to feed it to an unmanned orbiter for some reason.
Because we can’t predict with certainty what someone will do with the string summary of a star, this test story will focus on asking for that string.
It’s not hard to create the code which will make this story true, but it does involve a little more work than the simple copy and paste for
Yes, this is mostly a copy and paste of
The new details change the focus from displaying the details to returning them as a simple string.
What’s most important is that this test passes when handed Sol. How about some of those stars that don’t have proper names?
It does indeed work.
$ parrot setup.pir test t/01-extract-details.t .. ok t/02-summarize-star.t ... ok All tests successful. Files=2, Tests=4, 0.030 wallclock secs Result: PASS
I am concerned about the heft of this test code. The concern is that I had to split the CSV text, extract star details, and summarize the star manually for each star string. Each of those times is an opportunity for me to make a mistake. I think I would like to have a subroutine which would take the header fields and a CSV line of star data, and return the extracted details. Let’s make a new test story for that.
The code to make this work is simple enough.
I do not know if I have saved much work, but it is easier for me to read the test story. That is important to me, because debugging and improving the code is easier if I can easily understand what is happening.
It is time to take a break. The script we wrote a while back is evolving into a tested library that can be used by others. Now that we have our foundation, we can start building up. Our next step will involve adding some simple search behavior to the library, and that is a significant improvement. For the moment, take a little time to relax.