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 all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Paul Clapham
  • Bear Bibeault
  • Junilu Lacar
Sheriffs:
  • Jeanne Boyarsky
  • Tim Cooke
  • Henry Wong
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • salvin francis
  • Frits Walraven
Bartenders:
  • Scott Selikoff
  • Piet Souris
  • Carey Brown

Need help for the book - Practical Unit Testing with JUnit and Mockito

 
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator
I made a post for the book at - https://coderanch.com/t/641474/Testing/Junit-Testing-method-inputs-legal . I am in chapter 4 exercises and I am confused. I don't think I am learning much. There is no guidance and feedback. Please help me. If possible, please review my code too.

The ugly code so far -





 
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Report post to moderator
See my reply in the other topic. Before we go on, do you want to continue in this topic (in which case I will lock the other one) or continue in the other topic (in which case I will lock this one)?

Edit: After going through the code you posted here, I think we should just continue in this topic. I'll lock the other one.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

If you run this test with input an implementation that you would expect to fail the test, you get messages that don't make a lot of sense. First, the test code has bugs on lines 16 and 18. Second, if only the minimum or maximum value is expected to fail, the message will still mention the other one that passes. This is why this test has a smell and why you were advised to separate the tests for minimum and maximum. Also, I don't think you should use the JunitParamsRunner features right away. You should try to write simpler tests first then refractor them to use JunitParamsRunner when you start seeing a pattern and duplication in your tests.

Edit: come to think of it, if you ended up with a test like this, it would suggest to me that you didn't do the Red part of the TDD cycle and see the test fail first.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
I have a number of other comments about your test and main program code but will hold those back for later in this conversation. I think it will be worthwhile for you to start over and do your TDD cycle here, step by step, so we can get a better sense of your understanding of the technique. That way you can get proper guidance and feedback and the experience will more closely approximate one that you would have if you were doing pair or mob programming, which IMO is the best way to do TDD.
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator

Junilu Lacar wrote:I have a number of other comments about your test and main program code but will hold those back for later in this conversation. I think it will be worthwhile for you to start over and do your TDD cycle here, step by step, so we can get a better sense of your understanding of the technique. That way you can get proper guidance and feedback and the experience will more closely approximate one that you would have if you were doing pair or mob programming, which IMO is the best way to do TDD.



Thanks for your comments Junilu. I tried the TDD rhythm and got the code below. Does this look good ?







NOW adding checks for illegal min max values –





 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
  • Report post to moderator
Honestly, this would not pass a code review on my team -- we would rip this code apart. However, since you're a rookie I would say it's about as good as you could expect especially since you're doing it alone, without anyone to pair with.

Based on the code that you've written, here's what I think you're missing from your TDD practice:

1. The conversations about design and refactoring.

2. The feedback you get from these conversations at every single step of the way. That is, with almost every line of code you write, your pair or team, if you're mob programming, will tell you something. You're missing this. If I were to hazard a guess, you probably write more than 10 lines of code before you run/rerun your tests. We do it at a much finer grain than this. Think writing 5 lines of code MAX, then run/rerun the tests. Another 1-5 lines of code, run the tests. Another 1-5 lines of code, run the tests. All the while talking about design and the big picture.

3. Refactoring. There are a bunch of things that need to be refactored in this code and it doesn't seem like you have developed enough of a sense of code smells to know what these things are. Again, you're sorely missing out on the experience of having someone tell you that your code has a smell immediately after you write it.

If you really want to see what TDD is like, throw away everything that you've done and start over. I can be your partner. I will give you feedback at every step but you have to be patient and not get ahead of yourself or the process.

If you are ready to do that, then first start by replying with the general requirements for this password validator. What exactly does the book ask of you in this exercise? It should be fine to post verbatim what's in the book (I'm expecting my copy to be delivered tomorrow, BTW).

The next thing to do is come up with a rough initial design to help form a general idea of the solution. This will likely change as we go along and write tests and real code but you have to start with at least an idea of the initial design, even if it's just a straw man.

And DON'T write any code yet!
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
  • Report post to moderator
Here's another thing that should help you write better tests: http://agileinaflash.blogspot.com/2009/02/first.html

Read that article and keep those things in mind when you're TDD'ing.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
  • Report post to moderator
Jeremy, I got my copy of the book today and quickly skimmed through to Chapter 4. The exercise you are referring to is pretty open-ended. All it says is that you are to implement a method to verify password strength. It suggests that the password should be X characters long, has a mix of lower and uppercase letters, etc. but the exact validation rules that you apply are really left up to you.

The author touched on something I think is very important to understand but it may not have resonated with you as much. In section 1.3, the author writes about verifiers vs designers. The author admits to leaning towards the designers camp. I definitely belong to the designers camp. If you read enough blogs out there, you may notice a disturbing trend of people lashing out and railing against TDD. This includes a number of well-known and influential figures in the development community. Stalwarts like Kent Beck, Martin Fowler, Robert Martin, and Ron Jeffries are still very steadfast in their belief in TDD though and that gives me great comfort.

Anyway, I believe that most people who swear by TDD are in the designers camp and that criticism of TDD is directed mainly from experience with people from the verifiers camp. So, if you're going to see how TDD can really benefit, I think it's better if you approach it with a focus on coming up with a good design rather than verifying that your program works as you want it to work.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
  • Report post to moderator
Another passage from the book that you should note:

Tomek Kaczanowski wrote:By writing tests first, you have a chance of creating an API that is convenient for a client to use. Your test is the first client of this newly born API. This is what TDD is really about: the design of an API.


To reiterate what I wrote in the other thread, the test code is telling me there's a smell in the API of your validator class. The problem is, you probably can't discern what that smell is yet nor do you know what to do to get rid of it. That's where pairing with someone else can help.
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator

Junilu Lacar wrote:
If you really want to see what TDD is like, throw away everything that you've done and start over. I can be your partner. I will give you feedback at every step but you have to be patient and not get ahead of yourself or the process.
If you are ready to do that, then first start by replying with the general requirements for this password validator.
And DON'T write any code yet!




Hi Junilu ! Thanks for offering to pair with me. I appreciate it very much. I had that feeling that I really had no detailed requirements. I am not sure how to proceed. Maybe, I can
imagine myself as the user and think what I'd expect and list the requirements verbosely ? I am just deleting all that code before I learn and reinforce wrong things and end up going
back to TPD - Test in production development :P
 
Master Rancher
Posts: 4663
49
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

Junilu Lacar wrote:
(snip for brevity)
Anyway, I believe that most people who swear by TDD are in the designers camp and that criticism of TDD is directed mainly from experience with people from the verifiers camp. So, if you're going to see how TDD can really benefit, I think it's better if you approach it with a focus on coming up with a good design rather than verifying that your program works as you want it to work.



This, this this.
It's something that took me a while, and I only really grasped it when I did some BDD work using Cucumber, which sort of forces you completely away from thinking in terms of code. It allowed me to look at something like JUnit-based TDD in a clearer light.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

Jeremy Moore wrote:...going back to TPD - Test in production development :P


No, we don't want you going back to the dark side... There's actually a little meme thing making the rounds about that: It's a picture of the Dos Equis man with the caption "I don't always test my code, but when I do, I do it in production."

The requirements that you started with are good enough for now. We can add more to the mix later. That way, you can simulate how you get many requirements in the real-world: piecemeal over time.

I usually start TDD by whipping up a ToDo list. We maintain this ToDo list as we think of more tasks and complete tasks. When we're developing, we want to focus on one thing at a time but that's difficult because there are so many things that pop up as we keep learning more about the problem and the solution. The ToDo list helps us keep our focus and remember all the important but distracting ideas we get as we go.

First thing that goes on the ToDo list are some initial ideas for tests. Based on what you had before, here's what your ToDo list might look like for this problem:

1. Validator should have a constructor that sets minimum and maximum length
2. Validator should check for a minimum number of uppercase letters
3. Validator should check for a minimum number of special characters

My first response to this list would be: #2 and #3 on the list seem reasonable. #1 is dipping prematurely into implementation though. How about we start with a minimum length for now? And how about we state the ToDo differently? Kind of like how #2 and #3 are stated. Something like:

1. Validator should check for a minimum length.

When we get it to do that, we should be able to add the flipside pretty easily, right? Now our ToDo list will look like this:

1. Validator should have a constructor that sets minimum and maximum length
1. Validator should check for a minimum length.
2. Validator should check for a maximum length.
3. Validator should check for a minimum number of uppercase letters
4. Validator should check for a minimum number of special characters

What just happened here? Well, you just got a taste of what we hopefully will be doing a lot with the code: refactoring. We refactored the first item we had on the ToDo list and broke it up into two items. We also abstracted it out by removing some implementation details (no mention of constructor and its parameters). I loosely call this "refactoring" because we didn't change the essence of the requirement, we just changed the focus from implementation to intent. All we have now are statements of the behavior that we'd like to see from the Validator. We might even go one step further and say, "Validator is a good name but it's an implementation detail. Let's remove that, too." So now our ToDo list would look like this:

1. Validator should have a constructor that sets minimum and maximum length
1. Validator should check for a minimum length.
2. Validator should check for a maximum length.
3. Validator should check for a minimum number of uppercase letters
4. Validator should check for a minimum number of special characters

Shying away from implementation details as long as we can is something we do a lot on my team. It helps us from committing and becoming attached to one particular solution prematurely. We want to keep as many options on the table for as long as we can. This way, it's easier to extricate ourselves from any corner we might paint ourselves into later.

==========
SIDEBAR: One important tool to have when you're doing TDD is a version control system like Git or Subversion. You should be committing very frequently. I recommend committing every time you get a Green bar. This makes it easy to back up to a previous version if you do find yourself painted into a corner.
==========

Now that we have a short list of tests to write, it's your turn to do something. What would you do next? And remember, if you respond by writing more than 5 lines of code, I'm going to ask you to back up again.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
  • Report post to moderator
Another sidebar...

Ron Jeffries recently blogged about his thoughts on emergent design and self-organizing teams. Ron's post made me reflect on my own thinking about TDD and emergent design. It's a great post but one I'm afraid only those who have experience with self-organizing teams and emergent design will get. I responded to him on Twitter, saying that those who do will get it but it's too Zen for those who don't. You can't learn how to balance a unicycle by reading about it. You have to actually do it. The learning comes, as Ron puts it, from "being in the moment." Hence, it's very difficult to learn TDD from a book without someone else to provide a different perspective and provide checks and balances to your own, perhaps biased or incomplete, perspective. It took me a week and many falls, cuts, and bruises to learn how to ride a unicycle. It took me almost two years of self-teaching and solo practice to get reasonably comfortable with TDD. You get TDD the same way you get to Carnegie Hall: practice, practice, practice!

I just noticed this but where I wrote "First thing that goes on the ToDo list are some initial ideas for tests," I could have written "First thing that goes on the ToDo list are some initial requirements," but I didn't. Yet I later wrote "we didn't change the essence of the requirement." Reflecting on what was going on in my head as I wrote that, I think that now my first focus is on the tests. When I see a "requirement", I automatically think "How do I test that?" Given our ToDo list, someone with a different mindset might see them as a set of requirements. I look at them and see a set of test cases that define how the program is required to behave.

Earlier you asked how this book could have gotten a 5-star rating on Amazon. I actually think it's quite worthy of that rating from what I've read so far. Re-read sections 4.4, 4.6, 4.7, and 4.8. You may not get all of it now but keep what he wrote in mind and I think once you've experienced "being in the moment" of TDD, you will.
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator
Hi Junilu ! Here are my first "five" lines. Please tell me if this is okay. Thanks.





FAIL !!! Fixing just enough to make it pass.



Success ! Now, adding more test inputs in the next post to see if this "quick fix" will work for multiple inputs or not.
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator
Next Cycle -



Failed all cases except 5. Fixing that below...



Great ! Now all cases fail, even 5. Ensure that the value is being actually set.



GREEN ! What do I do next ? Should I create a test that checks of illegal input values for min, such as 0,-1,-7 etc. ?
The next steps in mind are - Check for illegal min values. Then add a max check. Then check if the max being set is >= min....more later. How does this sound ?
Thanks.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
This is a better start. But as I warned you before, I'm going to back you up if you get ahead of yourself. Don't worry, I won't ask you to throw everything away again. Just most of it.

I would have been like "Yay!" if you had posted just the following as your code to seed the first TDD cycle:

Ignoring the package declaration, import statement, blank lines, and lines with only closing braces, this would have been 4 lines of code. Just this much code is enough to get your first failure. This is how small you want your TDD steps to be.

At this point, Uncle Bob Martin would say things like:

"This is enough because compiler errors are also failures."
"This failure is the test's way of telling you that you have permission to write some production code."

I suggest you commit the code you have written so far to Git or SVN (I mentioned that a version control system is an indispensable tool if you're going to do TDD, right? I'll assume you set one up. If not, go ahead and do that right now). Then back it up to just the 4 lines of code above.

I'd like you to do this...

[ go ahead, do it right now! ]

... because you need to actually feel uncomfortable programming this way. Feeling uncomfortable is a good thing when you're trying to learn a technique like this. I like to think that this kind of discomfort means that some neural pathways in the brain are being broken and it's trying to defend against that but at the same time, it's also preparing for new neural pathways to be established.

I imagine some part of the brain saying something like "How can something like this that feels so wrong be right? Must... go... back... to comfort zone and write more code!"

Don't give in to that part of your brain. Think of that part of your brain right now as a wuss that you must break and toughen up by teaching it how to like writing code in small chunks like this.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
  • Report post to moderator
Feeling uncomfortable yet? Assuming the answer is "Yes", that's good. Let's continue.

When you get that compiler error, that's your first failure and your first opportunity to write production code. An IDE like Eclipse or IntelliJ IDEA makes this really easy to fix. That's fine but Tomek has a good warning that you should note: be careful about using the autocorrect feature of your IDE. Sometimes it writes too much code and short-circuits the TDD cycle. Just remember that as you fix this compile error with your IDE.

Now you have 1 more line of code, again excluding the same things as before from the count
That completes TDD step #2.

Now compile and run your test. You should get your first GREEN BAR.

Still feeling uncomfortable?

Yes... Give in to it... Use it... (evil Sith Lord laugh)

Now what?

TDD mantra: RED - GREEN - REFACTOR

RED - got compiler error. Check!
GREEN - fixed the error, compiled, and ran. Saw JUnit green bar. Check!
REFACTOR ... well, what's there to refactor?

I keep telling people this, but names are very important. Read the name of the class you wrote out loud.

Yes, out loud so you can actual hear what you wrote.

And don't listen to the wuss part of your brain telling you that it's silly. What's there to be embarrassed about? If you're going to be a real programmer you have to get used to people giving you funny and maybe somewhat fearful looks. Tell whoever it may be that it's OK, you're just reading back what you wrote to check for spelling errors.

No, don't use any of those phantom vowels and consonants that the wuss part of your brain is providing to your tongue to enunciate. Don't let it trick you into cheating on this. Read it out loud, phonetically, like a first grader would read it.

Sound funny? Like you're mumbling something? Like you've got some marbles in your mouth? Yeah, that's what a code smell makes you sound like.

So rename the class so that when you read the code out loud, you don't sound like you have cotton balls in your mouth. And rename it to something that makes sense. Like for an object. Like a noun or noun phrase.

I jokingly said somewhere around the Ranch the other day that it's really hard to train a dog if you name it "Stay". "PasswordCheck" is kind of a borderline name for a class. It's not really bad but I think you can do better. In fact, I know you've done better because you've already used a perfectly good name before.

Once you've completed the Refactor - Rename operation in your IDE. Run your tests again. You should see a green bar again.

Always run your tests again after the refactor step.

Now you are ready to go to the next TDD cycle.

----
Let's pause here for a while and retrospect. How did you feel doing all I've asked you to do so far? What's going through your mind? What kind of doubts do you have? Did you do the Git or SVN thing like I asked you to? Did you commit again after you refactored and got a green bar again?

 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
Here's one thing to retrospect about: Tell me what you think about this statement: "A failure is the test's way of saying that you have permission to write some production code."
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator

Junilu Lacar wrote:Here's one thing to retrospect about: Tell me what you think about this statement: "A failure is the test's way of saying that you have permission to write some production code."



Hi Junilu ! I am using Git with eclipse. Its so easy for a 1 man project ! Point and click. I could not even setup SVN to do all this. I tried visual svn server, tortoise svn etc and achieved nothing. Now the only problem is I don't know
how to view older versions of my code in Git Eclipse. I need to do that so that I can post all the small changes I made so far. Please help me to get this first. Thanks !
I am able to see the older commits using the steps -
right click any java file/resource > team > show in history > double click any revision in the history window that opens up. The only problem now is - I can open individual files and see their older versions using the steps I gave above.
But, when I select the whole project and do the same thing, it does not work. Please tell me how I can do it for the whole project.

Anyway, here is what I did so far -



PWD CHECK -



 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
Jeremy, that's a great effort at trying to make smaller steps. However, you're still giving in to the urge to write more code than necessary and you're still skipping the REFACTORING step. Let me step through how this TDD conversation might go in real time. Humor me just a little bit.

STEP #1 - start with the "seed code" that I gave, nothing more. No JUnitParamsRunner. Just the following, AS IS, and literally nothing more.
First failure: Compiler error - unknown class 'PwdCheck' on line 9.

STEP #2 - use autofix to get rid of compiler error. You get this generated code:
Compiler error fixed. Flip back to PwdCheckTest and Run as... JUnit Test. GREEN bar!

Step #3 - Refactor

Jeremy: "Ok, now I'll add a setter for the minimum length."

Junilu: "Wait. That name kind of sounds like you're mumbling. Let's rename it to make it clearer to read."

Jeremy: "What name? PasswordCheck?"

Junilu: "You're cheating. You're enunciating phantom letters that aren't really there. Read it phonetically, like a first grader."

Jeremy: "Ok. Puh.. Wuh.. Duh.. Check. Huh."

Junilu: "See what I mean?"

Jeremy: "Yes, I see what you mean. But it's easier to type the shorter name."

Junilu: "But it's harder to read, so suck it up. Besides, you have autocomplete in Eclipse. Learn how to use that feature. It'll save you some pain. Including the hurting you'll get from me if you keep making me read your mumbling." (Uses the tip of his thumb to give a playful but sharp poke to the pressure point below Jeremy's armpit)

Jeremy: "Ow, that kind of hurt!"

Junilu: "Sorry. Just a little reminder that I have a black belt in Aikido and I can literally hurt you if I need to."

Jeremy: "All right! Sheesh, no need to get physically abusive, you know. I'll rename it to what I had the first time: 'PasswordValidator'." (In Eclipse: Refactor... Rename)

Eclipse renames source file from PwdCheck.java to PasswordValidator.java, changes the class name:
Jeremy: "Now what? You need to teach me some of those Aikido moves... but promise not to hurt me."

Junilu: "Come to the dojo. It's fun. I promise not to hurt you if you promise to choose readable names. No more keystroke-saving names from now on. Vowels are our friends. Ok, let's get back to it. Remember to run the tests every time you refactor."

Jeremy: "Oh yeah, right." (Runs test again)

Still GREEN bar.

Jeremy: "Cool. Now I'll add that setter for minimum length."

Junilu: "Wait. Look at your test class name."

Jeremy: "Yeah, what about it?"

Junilu: "You're still mumbling... Puh.. Wuh... Duh.. CheckTest."

Jeremy: "Huh, I didn't notice that. Ok, I'll rename that too." (Refactor... Rename...)

Eclipse renames source file to PasswordValidatorTest.java:Jeremy: "And now I'll run the tests again." (runs tests, GREEN bar)

Jeremy: "NOW, can I write a setter method?"

Junilu: "Did you commit the code to Git?"

Jeremy: "Ok, I'll commit it now." (commits to Git)

Jeremy: "Setter method for minimum length? Now?"

Junilu: "Why do you think you need a setter method? Besides, we're not done refactoring yet..."

Jeremy: (WTH else could there be to fix?!)
...

(END of simulated TDD conversation. For now...)

Do you see why you should take it in very small steps?

Again, this kind of banter between a programming pair is what you're missing and that's keeping you from experiencing and seeing the true nature of TDD. The value is really in the conversations that you have as you write very small increments of code. This would have happened in less than 2 minutes in real time but you have to be very patient in this medium.

Experienced programmers who may be following this are likely to be thinking "That would just aggravate the heck out of me! How can you get anything done that way? It's like working with Monk, that O-C detective on TV!"

Sure, it does feel slow at first and a lot of people get aggravated by the seemingly obsessive compulsion to refactor ruthlessly. But after a while, after you've seen the truth in Uncle Bob's quip that "The only way to go fast is to always go well," then you start becoming refactoring- and test-obsessed, too.

So, are you ready to move forward at this snail's pace? If you are, then answer the questions: "What else can we refactor and why do you think you need a setter method?"
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator

So, are you ready to move forward at this snail's pace? If you are, then answer the questions: "What else can we refactor and why do you think you need a setter method?"



Thanks Junilu ! I don't mind anything as long as it makes me a better developer. I don't really see any room for refactoring now. Am I missing something ? I need a setter method to implement
requirement " Validator should check for a minimum length. ".
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
We can give the test a better name. If you follow BDD (behavior driven development) conventions prefer something like should_check_for_minimum_length. I personally don't put "should" but that's just me. Again, the name should be readable. You'll see how it helps when you start looking at test reports with the names listed out.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

Jeremy Moore wrote:I need a setter method to implement requirement " Validator should check for a minimum length."


Nothing is telling you that. You have to stop preempting the test code.

Remember what Tomek wrote: it's about designing a good API so start from the main API, not the support API. Setter is part of the support API. What is the method that will give you the ability to check a password? setMinimumLength does not give you that ability. So write one line of test code that will serve as an example of your main API method.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
Let me explain what I meant by preempting the test code. Uncle Bob Martin has three rules of TDD:
1. You may not write any production code unless you have a failing test.
2. You may not write more test code than is sufficient to fail.
3. You may not write more production code than is sufficient to make a test pass.

So before you write any production code, like a setMinimumLength method, you have to write a minimum amount of test code that is going to fail. Minimum is one line of test that exercises the main API. Again, setMinimumLength is NOT part of the main API. Think about what the test code will look like first. Think about production code only when you have failing test code. Let the test code tell you when you can think about production code. Don't preempt it.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
So here's where we would be so far, after maybe 1-3 minutes of real-time TDD, depending on how much banter we have back and forth:

PasswordValidatorTest.java:
PasswordValidator.java:
You might think, "That's it? How unproductive!"

On the surface and to the uninitiated, this may seem true. But consider this: in these few minutes, we've already had a code review and refactored some names that were less than ideal, we've made the code more readable, and we've come to an agreement about the kind of names we're going to use going forward. If we hadn't had that micro code review, imagine the amount of rework you would have created if you had kept coding for another 10 minutes, making the same kind of "mistakes" that we caught in the first few minutes of our TDD encounter.

How much more rework would there have been if you had gone on for 30 minutes without feedback from TDDing? One hour? Half a day? A whole day? Some developers will code away for a few days--or even week(s) or month(s)!--before anyone else gets a chance to just look at some of that code. Can you imagine how much time it takes to find all the different things that could have been messed up during that much time of solo development? Very few people can write perfect code at one go and the few who claim they can are either pathological liars, delusional, or both. In the long run, solo programming is usually much more wasteful than pair or mob programming. TDD is the vehicle that enables pairs/mobs of programmers to be much more productive than their soloist counterparts.

TDD helps cut down coding and design waste that is usually produced by non-collaborative work. When done well, it cuts down waste dramatically. That's the key though: you have to do it well. Improperly executed TDD is just as, if not more, wasteful as solo or non-collaborative development.

There's a quip that I like that could be offered to skeptics and critics of Aikido who say that it's hokey, choreographed, unrealistic, impractical for use in real-world situations. It goes like this: "Aikido works; your Aikido doesn't."

By the same token, to those who say TDD doesn't work, is a waste of time, and doesn't really have any worthwhile benefits, I wish I could offer them this: "TDD works; your TDD doesn't."

We shouldn't really say that to people though. Not for Aikido, nor for TDD. That's not a very good approach. The best way to silence the critics is to show them how it works. That's why it's kind of fun when we get one of these guys who think that Aikido is "for the birds" in the dojo and we apply the yonkyo technique* on them. The technique takes very little effort on the part of tori (the person performing the technique) but can be excruciatingly painful for the uke (the one receiving the technique). They'll usually get up off the mat or from their knees, shaking their arm trying get the feeling back in their hand and they'll look at you as if wondering what the heck just happened.

Yonkyo is fairly easy to apply once you know how to do it but it's difficult to "get" if you don't know exactly what to do with the hand that is key to applying force to the specific pressure point involved. It's very frustrating for beginners and it's kind of amusing to watch them fiddle around with their partners arms, trying to find the right knuckle to use and the right placement on uke's forearm. My sensei says that his sensei has an incredible yonkyo. After putting it on you, he'd often laugh and ask rhetorically, "It's terrible, isn't it? Makes you want to throw up, right?" My sensei would just answer "Yes sensei, that's about right." But afterwards, you come away no worse for wear but with a healthy amount of respect for these old guys who can put you down on the mat in a second, and keep you down indefinitely, with just one hand.

The point to all that was that it's kind of like that with TDD.

* Video shows yonkyo being performed by Christian Tissier Shihan, 7th Dan in Aikido
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator
Thanks Junilu. Now, I am pushing all my code to GitHub to make things easy - https://github.com/jmoo007/JunitBook
Does this look good ?
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
I see that it's going to take a while to break you of this fascination with JUnitParams Stop doing that for a while. I'm trying to guide you to the spot in the TDD conversation where it's going to make sense to make that choice. If you keep preempting me on that, you'll miss an important learning moment and I'll miss out on a teaching moment. I promise you, it will make more sense when we actually get to those crossroads.

For now, let's pretend you did what I asked you to before and just wrote ONE single line of code in addition to what we had so far, like so:

A minute or two of conversation would ensue from this one line of code. I imagine it might go something like this:

Junilu: "Ok, I guess pv is slightly better than p but we'll circle back to that later when we refactor. What's with the underscores in the method name?"

(Quietly writing down "Refactor name: pv" on the TODO list)

Jeremy: "Well, we're doing that for the test names, aren't we? I like these underscores, they're easier to read than camelCase."

Junilu: "Test code is special. We don't have to follow convention with test code. Use normal conventions for production code."

Jeremy: "Ok." (Refactor - Rename - checkPasswordSize)

[Notice how we skipped over quickly to REFACTOR - that happens sometimes]

Jeremy: "Ok, now do the quick fix..." (generates checkPasswordSize() method)

(still getting a compiler error: "pwd" is not defined -- Back in RED step now)

Jeremy: "Now, let me set up JUnitParams to fix that..."

Junilu: "Wait, what's 'pwd'? Are you mumbling in your code again?" (showing Jeremy his fist and at-the-ready nerve-jabbing thumb)

Jeremy: "Are we going to back to Refactor now? We haven't done RED and GREEN yet."

Junilu: "Ok, you got me there. It's not a strict cycle but for your sake as a newbie, we'll stay within the lines. I'll add that to the TODO list."

(Adds "Jeremy is mumbling 'pwd' again - refactor that habit out of him." to the TODO list)

Jeremy: "So, as I was saying..."

Junilu: "Wait, stop it with JUnitParams. We don't need that yet. The code doesn't tell us that we need it yet."

Jeremy: "What the heck do you mean, the code doesn't tell us? I tell the code what to do. I'm saying we need JUnitParams!"

Junilu: "You only think you do. The code actually has a lot of mysterious powers. It talks back to you but you just can't hear it right now. Maybe it's because you're still wet behind the ears or you're just too caught up in your obsession with JUnitParams that you can't hear it. Don't worry, you will. Eventually."

In RED with compiler error on line 10: 'pwd' is not defined

Junilu: "Introducing a variable in the test right now is premature. What's the simplest thing we could possibly do with this call?"

(END of simulated TDD conversation)

... cue the real-world Jeremy: Answer the question: What's the simplest thing we could possibly do to make the call to checkPasswordSize() without defining a variable?

======
Our TODO list looks like this now:
1. Validator should have a constructor that sets minimum and maximum length
1. Validator should check for a minimum length.
2. Validator should check for a maximum length.
3. Validator should check for a minimum number of uppercase letters
4. Validator should check for a minimum number of special characters
5. Refactor name: pv
6. Jeremy is mumbling 'pwd' again - refactor that habit out of him.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
BTW, please go ahead and rename the test method to "should_check_for_mininum_length" -- that way, if and when you do get into BDD, you'll already be used to naming test methods according to conventions.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
Also, do these little imaginary conversations help? Do they seem plausible to you, like if we were sitting together doing pair programming, do you see the conversation possibly unfolding the way that I imagine it?

That's kind of how I actually talk to the folks on my team, except we do all our pair/mob programming virtually using a collaboration tool that provides phone, camera, and desktop sharing capabilities. I don't get to poke at their pressure points though.
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator


In RED with compiler error on line 10: 'pwd' is not defined view plainprint?
Note: Text content in the code blocks is automatically word-wrapped




Junilu: "Introducing a variable in the test right now is premature. What's the simplest thing we could possibly do with this call?"



Is it just this ?

 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

Jeremy Moore wrote:Is it just this ?


Yes!!!

I think I just cried a little tear of joy.

Now we still have a whole mess of possible choices in front of us!

So what's the next step? Run the tests, right? We just wrote a little bit of test code that's sufficient to fail. We run the tests to actually see the test fail.

Failure is good. A failing test means we have permission to write production code. Writing production code is what we developers live for.

Next step: What is the least amount of production code that you could possibly write to make this failing test pass? Remember, it has to be the least amount, and the simplest and most straightforward code you could write. It can even be "stupid" code. It really doesn't matter if you feel it's the "wrong" code to write. We will fix that later when the code tells us how to fix it. I'll give you a hint: you only have to change ONE WORD in the production code.

I like to think of it as something like sculpting stone. You have this big, shapeless block of white stone. You very slowly and carefully chip away at the white parts to reveal the colored parts beneath. You keep chipping away like that until you have revealed the colorful, beautifully shaped statue that was there all along.
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator

Junilu Lacar wrote:
Next step: What is the least amount of production code that you could possibly write to make this failing test pass? Remember, it has to be the least amount, and the simplest and most straightforward code you could write. It can even be "stupid" code. It really doesn't matter if you feel it's the "wrong" code to write. We will fix that later when the code tells us how to fix it. I'll give you a hint: you only have to change ONE WORD in the production code.

I like to think of it as something like sculpting stone. You have this big, shapeless block of white stone. You very slowly and carefully chip away at the white parts to reveal the colored parts beneath. You keep chipping away like that until you have revealed the colorful, beautifully shaped statue that was there all along.



Thanks again Junilu. Change return false to true. After that, I am stuck. I am now thinking what to do next the current state of my code is -
https://github.com/jmoo007/JunitBook






I made the test pass. Now, I am thinking about this -

1. You may not write any production code unless you have a failing test.
2. You may not write more test code than is sufficient to fail.
3. You may not write more production code than is sufficient to make a test pass.

As per 1 - Should I make a test that will cause my validator method to fail ? Maybe another test method with "failure" in its name. If yes, then the name of my existing test method must have "success" in its name.
OR, junit params...I get the feeling its too early for junit params. Its been a while since I read my book. I think the author would suggest to write another test that just fails. Lets say i wrote the failure test and renamed the existing one. How would i make that pass ? Maybe add a check in my method - if parameter pwd length == length of pwd from failure test, then return false. Else return true ? hmm...that sounds okay to me. I think
I'll just do this soon and commit it to git. Gonna be away from my comp for a while...
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

Jeremy Moore wrote:After that, I am stuck. I am now thinking what to do next


This is no reason to get discouraged. In fact, you should be glad that you're saying this to yourself. This is a sign that a deeper understanding of the technique is setting in.

I see this in Aikido practice all the time. It's natural to not want to mess up. That's a perfectly human instinct. So, you'll see people who have started to "get" a technique start out doing it really well, then suddenly stop because they haven't gained the same level of understanding of the next parts as they have of the earlier parts. That's what is happening to you now.

You asked, "Should I make a test that will ... whatever... ?"

The answer is: No. You should think a little bit about the design before you write another test.

But before we proceed, delete line 12 first, the one where you declare message), and remove message as a parameter to assertTrue. You are again pre-empting the code and depriving yourself of a learning moment. Every line you add should have a reason and that reason should come from the code. Whatever you think your reason was for adding this message, it's not true. The code has not said it yet. Eventually, it probably will but until then, we wait.

Now, to elaborate on thinking "about design"...

What's the TDD cycle? RED - GREEN - REFACTOR.

What have you done so far? RED (assertion failed) and GREEN (changed false to true)

And then, what? You write another test? So, you're going straight back to RED?

No, right? You have to stop by REFACTOR first. Don't pass REFACTOR.

Refactoring is about making the code clean and improving the design.

That's two parts: 1) cleaning up the code and 2) improving the design.

First, see if there's anything that you can clean up in the code, besides deleting that message.

Next, ask yourself "What (small) aspect of the design is missing or not quite right?"

When there's a part of the design that's missing, you can write a test that is guaranteed to fail.

I like to ask myself "What other bug do I have right now?" because the answer leads to me to an aspect of the design or behavior that is lacking or missing.

Let me know what you come up with both of those: cleaning up, and making the design better or finding what's missing in it.

And remember to delete that message! You won't learn how to tell when and when not to put them in if you keep putting them in automatically.
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator
The test should be -



There is no test to check if you can set the min length in the validator. So, should I add a test for that now ?
I don't know why I chose "checkPasswordSize". Maybe I should rename that to checkPasswordMinimumSize.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

Jeremy Moore wrote:I don't know why I chose "checkPasswordSize". Maybe I should rename that to checkPasswordMinimumSize.



You may not see it but I find it amazing to see how much you've progressed. Who would have thunk it, that just a few lines of code and a LOT of lines of discussion could produce the kind of progress that I'm seeing in you right now. Well, I knew. I've seen it before. But it never ceases to amaze me.

What progress am I seeing? I'm seeing that you're starting to think about your design more instead of just blindly writing code just so you can. You're starting to pause and ponder what the right thing to do is. You're making progress from being a code-spewing machine to something meaner and leaner. Hang with me a little bit more and you'll start to see it too. Don't get cocky yet though.

There is no test to check if you can set the min length in the validator. So, should I add a test for that now ?


You still have a bit of a ways to go. -- The answer to the above question is, of course, "No."

So, were in the Refactor step of the second TDD cycle. This means we want to 1) clean up code, then 2) think about design.

In asking whether checkPasswordSize or checkPasswordMinimumSize works, you've gone to 2) think about design. Why isn't it cleaning up code? After all, you're talking about renaming, right? Because both names are clean. You're really asking which of those names make for better semantics for interacting with the password validator. That's design thinking.

But let's backtrack just a little bit to cleaning up. Look at the TODO list again. Item #5 on the list says to rename the pv variable. I'll leave it to you to decide whether or not you can live with that variable name. For me, it's unreadable. I would choose a nickname like checker or validator instead so I can actually enunciate the variable name but I'll leave it to you to choose which way to go. I just ask that you think about doing the right thing. To rename or not to rename? That is the question.

Then, going back to design thinking again, I would say something like "Before we rename that method, let's think about the semantics of the API a little bit."

What should client code that uses a PasswordValidator look like? Here's what I can imagine:

Does that seem reasonable to you? Can you suggest an alternative way to interact with a PasswordValidator?

This is where the value and fun in pair programming really comes out: when we work like this, conversations about design are constantly happening. The more you talk about design, the more you experiment with different choices, the more you learn about the software. The more we learn and understand about the software, the easier it is to work it and to work with it.

This is actually a key learning moment for you so take a little more time to think about what we're doing at this juncture. When you understand the points that I'm making right now, it will be a lot easier for you to get into the kind of mindset you need to succeed with TDD.

I'm going to go quiet for a few days, got some things I need to do for work. I'll try to check in on this thread when I can and keep our conversation going. Later.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
To give you something to mull over about design thinking, here are a few bits of the design conversation I imagine might ensue from looking at the sample API that I suggested.

"So we just want to call a check method and it will return a list of violations that we can check."

"Should we call these violations or weaknesses? We're checking for password strength, right? The opposite of strength is weakness, not failure."

"If we use weakness, that actually works better with the Star Wars references in those comments."

"What if the user is a Trekkie?"

"So now we have to create another class, PasswordViolations?"

"Who said anything about a class? I can't think of any significant and interesting behavior for a PasswordViolation class."

"Then what, an Enumeration then? Yeah, that makes sense."

"So we have to rename a few things now."

"Well, that's just sample code so we have an idea of where we're heading with the design. However, we do have to write some tests that can serve as sample code for the API users..."
 
Jeremy Moore
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
  • Report post to moderator

Junilu Lacar wrote:
Then, going back to design thinking again, I would say something like "Before we rename that method, let's think about the semantics of the API a little bit."

What should client code that uses a PasswordValidator look like? Here's what I can imagine:

Does that seem reasonable to you? Can you suggest an alternative way to interact with a PasswordValidator?



Thanks Junilu ! This makes sense. I was about to think of making a class called PasswordWeakness until I read the post after. Yes, Enumeration is the right choice.
I am wondering if I should have one public checker method which will make use of private methods to check min length, max length, max capital chars etc.
I'll think this through.
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator

Jeremy Moore wrote: Yes, Enumeration is the right choice.
I am wondering if I should have one public checker method which will make use of private methods to check min length, max length, max capital chars etc.
I'll think this through.


Sounds reasonable. Think it through - but not too much; overthinking the problem can be as bad as or worse than underthinking it. Then share some of your ideas. I already have a good idea of where I would take this but I'll let you drive for a while to see where you take us.
 
Ranch Hand
Posts: 954
4
  • Likes 1
  • Mark post as helpful
  • send pies
  • Report post to moderator
Thanks Junilu for wonderful support but unfortunately this talk doesn't complete. I wish this talk continues so that we can get a complete idea.

Is it possible that i should start this talk again from where it stopped in new thread as i see we still need some clarity on the design /re-factoring part?
 
Junilu Lacar
Marshal
Posts: 15877
265
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Report post to moderator
Sure, that's fine.
 
Everyone is a villain in someone else's story. Especially this devious tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
    Bookmark Topic Watch Topic
  • New Topic