I really want to use TDD (GOOS style) on some of my projects, but I struggle so much getting the first code written using TDD, with almost no design in place, that I always give up after some time. I’m trying to follow the GOOS flow: acceptance test à (unit test à implement, unit test à implement…). Let me explain by a contrived but somewhat representative example.
Task: create a class (no gui) that can fetch a list of grades from a website. The grades are fetched by logging in to a HTML form (HTTP post to a specified URL) with a provided username and password and parsing the output of the HTML page listing the grades. A grade consists of number (the grade itself) and the date it was given (a string).
So here we are with a simple requirement and NO design whatsoever. I must start by writing an acceptance test, the question is a test of what? Well the first test must establish the “walking skeleton” and must verify that my app can fetch grades from the "real" website. Also the only feature right now is to fetch the grades, so I can’t think of anything else to test:
AcceptanceTests.java:
public void canLoginAndFetchGrades() {
GradeFetcher g = new GradeFetcher(“http://someUrlWithLoginForm.com”)
List<Grade> grades =
g.FetchGrades(“someUsername”, “somePassword”)
// Check that all the grades are
correct
assertThat(grades.get(0).getNumber(),
is(equalTo(10)));
assertThat(grades.get(0).getDate(),
is(equalTo(“11-02-2012”)))
// .. and so on for the rest
I create all types so the test compiles and see it fail
because it just returns an empty list of grades. Now what? This is usually where
it breaks down for me: I have written the first acceptance test but don’t know
what to do next. This seems like a good acceptance test: when this test passed
the software probably works as intended.
I think I need to write a unit test before implementing any behavior, but the
question is what should I test? There is obviously a lot of functionality
needed for this acceptance test to pass: sending the correct HTTP request with
the correct headers etc, parsing the response correctly, inserting correctly
into arraylist and so on. I have a feeling that I want to test those in separation
so I can grow the application bit by bit, I just don’t know where to start.
In the end my confusion may be a result of a misunderstanding
of what an acceptance test is, when I should write one but most important what
to do afterwards. It may also be only that I just don't know what to do afterwards:)
I have always thought of an acceptance test as a “feature”
that is visible to and required by the customer (me, as a user of the API), and
I believe that is also the view in GOOS. In my example I would classify all these
as things that should be tested by acceptance tests:
- Can login and fetch grades
- Handle invalid credentials
- Handle invalid url
- Handle timeout (of HTTP request)
These “features” are what is visible to the customer. I
imagine that there are other things that are NOT relevant to the customer like:
- How exactly the login sequence is handled (what http request is sent, what
headers is in it etc)
- How the output on the HTML page is parsed
These things are obviously NOT relevant to a user of the GradeFetcher class so they are not acceptance criteria. They must however be tested by unit tests somewhere inside the system.
Yes, I agree with Matteo. Also, I would want to separate the login from the fetching. So two acceptance tests:
- Can login
- Can fetch grades
John D.
Ole,
I may have assumed too much, but I expected you would have, not only “fetching grades” but also other operations. All of which needed to be done while logged in. And that it would be possible to log in, and log out without doing anything.
So this would seem to need three stories:
- A user can log in with known account and password (and fail otherwise)
- A logged-in user can do an operation (if not, then they can’t)
- A logged-in user can fetch grades (I’m going to fake a “logged in” state, credentials or whatever to make this pass)
John D.
From: growing-object-o...@googlegroups.com [mailto:growing-object-o...@googlegroups.com]
On Behalf Of Ole Rasmussen
Sent: 24 January 2013 20:17
To: growing-object-o...@googlegroups.com
Subject: Re: [GOOS] Project start: what to do after first accept. test? (example)
JohnD:
--
--
--
Let me first remark, that using canned data serves a special purpose,
overcomming my initial paralysis and kickstarting my TDD process. It's a
training wheel I hope to get rid of eventually. As every technique, it
has its drawbacks. The goal should certainly be to write code without
canned data. But for now, I find this technique helpful.
Maybe a @WithCannedData (or @WorkInProgress) annotation would be useful.There's a a project called Pending that provides annotations and JUnit rules/categories to mark tests as "pending". It is slightly different from "canned" because it mutes failure reports for tests that are expected to fail.