• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Paul Clapham
  • Tim Cooke
  • Jeanne Boyarsky
Sheriffs:
  • Rob Spoor
  • Devaka Cooray
  • Liutauras Vilda
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Tim Moores
  • Mikalai Zaikin
Bartenders:
  • Piet Souris

TDD tests for the current TDD-event 'ShoppingBasket'

 
Bartender
Posts: 5443
212
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
As promised, here are some tests that I would use for my TDD. I hardly ever use TDD, and that shows, no doubt!

In the code, I made a few assumptions:

1) all prices are in BigDecimal
2) an Item just has a name (String) and a price (BigDecimal) (BigDecimal, since I do not know how precise testing is with doubles)
3) the basket.add is overloaded

Is it right to nake such assumptions?

Tests:


 
Sheriff
Posts: 17616
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The most important thing about TDD for me is the conversations you have as you go. They reflect the level of shared understanding that's evolving as the code grows and evolves during the process. That's what I'm trying to glean from the code as I'm reading through it. What was the conversation around the decision to overload the basket.add() method? Was it as a convenience added to facilitate writing tests? Or was there a thought of having the quantity be an input from the user, e.g. an input field on the order page where the user could specify how many of each item they wanted to buy, like what you see on Amazon?

What was the thinking around naming the test totalShouldBe5Euro90ForTheseItems? Did they really examine the implications to the overall test story by using that name? How did the idea of "Euro" suddenly come out? Nothing else in the test code says the currency is Euro, so why is the specific currency now part of the test story? The set up doesn't mention Euro at all, so how do we know the currency is Euro?

These are all things that engaged pairs/mobs would point out to each other and refactor accordingly. The funny thing about this, now that I think of it, is that to the curious designer and practitioner of TDD, the code gives off a vibe that says those conversations didn't happen. I guess that's another heuristic I use to detect code smells.
 
Piet Souris
Bartender
Posts: 5443
212
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Well you have to assume some functionality, how would you otherwise write even one test? For instance, no add-method was required, yet in the tests sofar it was used.

When writing a test with add;add;add; the idea soon comes up to make that more userfriendly by assuming a method add(item, quantity), or even add(item...). So, in order to pass that test, there better be such a method. Now, I wonder, is that TDDevelopment or TDDesign or is that defeating the whole TDD-idea? I don't know.

As for the use of the word "Euro", that was just to indicate that the test was about money, or at least something numerical. You are right: there was no discussion and no sparring partner. That's why I put part of the tests that I could think of on the forum.

Another thing I noticed last Tuesday was that because of the discussions, the progress was slow. Might it be an idea to write tests all on your own, and do the discussions afterwards?
 
Junilu Lacar
Sheriff
Posts: 17616
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There were, and perhaps still are, people who preferred "Design" over "Development" when spelling out TDD. The way I think of it is that design is not really a distinct phase in the development process. When I'm writing a test first, I'm really writing a detailed design specification. When I'm refactoring, I'm improving the design with a focus on clarity and cohesion. When I'm writing the production code, I'm realizing my design ideas and making them concrete and executable. So design is spread out all over entire process when I'm developing.

Jack Reeves was very influential in forming this perspective. I often go back to his three essays to remind myself and get validation.
 
Junilu Lacar
Sheriff
Posts: 17616
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Piet Souris wrote:Well you have to assume some functionality, how would you otherwise write even one test? For instance, no add-method was required, yet in the tests sofar it was used.


True, just as there is no explicit requirement to create a basket but you still have to provide a constructor. Again, what's important is to have a conversation about each decision, no matter how small. That puts everybody on the same page or trying to get there.

If I use my imagination a little, the conversation might go something like this:

P: Now we want to have a test querying the quantity when there is something that matches in the basket.

J: Ok, we can write

P: We're going to need a way to put an item in the basket though.

J: Ok, how about we provide an add() method for that?

P: But don't we need a test before we put that method in the production code?

J: Good call. Let's start with a test for add() then:

J: There, how about that?

P: But now we're going to need a test for isEmpty() first, don't we?

J: Dang, this is like a pulling a loose thread on a sweater, it just keeps unraveling. You're right though, so how about we do this:

J: There, we finally got to the bottom turtle. For a minute there I thought this was going to be turtles all the way down. Let's work our way back up the stack now.

... and so on.

Why don't we try this tomorrow in our next session?
 
Junilu Lacar
Sheriff
Posts: 17616
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Piet Souris wrote:Another thing I noticed last Tuesday was that because of the discussions, the progress was slow. Might it be an idea to write tests all on your own, and do the discussions afterwards?


Yes, this is one of the hardest things to get past when you're just starting out with TDD. There's going to be a lot of discussion, back and forth, hemming and hawing, agonizing over every little decision. Part of it is psychological, I think, stemming from the need to be right or an aversion to being wrong or looking stupid. Effective teams get over that. It's like having a warrior's mindset going in: You're already dead, so might as well accept your fate and just go into battle and do your job. That attitude is ironically what gives you the best hope of surviving.

So go into a TDD session thinking, "I'm going to look stupid today, so I might as well accept that as a fact and just do whatever comes to mind. My pair/mob partners will call out my stupidity and we can always make it better." Although I like to think of it not as "my stupidity" but "the code's stupidity" -- once the idea leaves your head and goes out through your fingers and into the code, then it's its own thing, an entity that stands on its own merit, with no connection to its source. Now you're judging the code objectively without any ego attached to it. This is when you can really change code fearlessly and ruthlessly.
 
Piet Souris
Bartender
Posts: 5443
212
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I've said it a couple of times before, but your ability to make complicated things look easy is really amazing...
 
Saloon Keeper
Posts: 15110
344
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Junilu, do you have a link to the assignment somewhere? I'd like to put some time on the clock and also see how far I get with the test cases.

Another thing that lingered with me after the session is that we really focused on writing one test at a time and then making that test pass before moving on. What's your position on writing many tests at once before moving on to making all of them pass?
 
Junilu Lacar
Sheriff
Posts: 17616
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The kata is here: https://sammancoaching.org/kata_descriptions/shopping_basket.html

The general advice is to write one test at a time and go one round of Red-Green-Refactor. When you have a bunch of tests you think of, you can note them down in a TODO list. That's usually the role of the Scribe -- a mob should have one, to capture interesting details so they don't fall through the cracks.

If you're doing BDD, which some say is "TDD done right," then you'll probably be writing a bunch of tests up front. But that's a whole 'nother focus from test-by-test TDD.
 
Junilu Lacar
Sheriff
Posts: 17616
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm also going to try to do this exercise solo. The one thing that's still bothering me is when we changed the API to a method and it broke multiple tests. I'm going to try Piet's approach of starting with Item and getting its API well defined first.

The theory I want to test is if you start with a class that doesn't have dependencies, then you're less likely to break things and get into a Yak Shaving expedition. When we start at ShoppingBasket, there's going to be a tendency to Yak Shave because it's dependent on Item. It seems like it's best to start TDD with a more stable class, then work your way up the dependency chain.
 
Stephan van Hulst
Saloon Keeper
Posts: 15110
344
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Keep in mind that one of the things that "went wrong" was that we added a test for the Item's name() property, but we forgot to run that test before we moved on. Our new tests were based on code that was already unstable.
 
Junilu Lacar
Sheriff
Posts: 17616
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
One lesson learned for me is that if you're practicing:

1. Practice on a separate branch
2. Commit changes every step of the way, especially when you've broken something. This will facilitate remembering what happened and doing forensics at a later time.

I need to figure out a good way to manage the repo such that
1. Practice branches are easy to go back to and replay for learning purposes
2. There's not a ballooning-out-of-control number practice branches that never get deleted
3. Lessons learned from each practice branch can be easily perused
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
reply
    Bookmark Topic Watch Topic
  • New Topic