This is the article on the 10th day of Inatatsu Adventar.
Continuing from the last time, this time is the participation note of Do Mob Programming in Kansai ~ Hin ??.
This time, I will write what TDD looks like based on the facts (slightly modified) the flow when I actually performed TDD for the first time.
Start pytest, FizzBuzz in the same way as previous article. If you test it and get angry, then implement it so that you don't get angry.
The test passed and it became Green. Up to this point, it is the same as last time.
You have now confirmed that the test passes if it meets your requirements.
Now that the hiker is obsolete, let's rewrite hiker.py and test_hiker.py to fizzbuzz to meet this request.
There is no module like hiker! I was angry. Let's rewrite it according to various fizzbuzz.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
test_fizzbuzz.py
import fizzbuzz
def test_life_the_universe_and_everything():
'''a simple example to start you off'''
douglas = fizzbuzz.FizzBuzz()
assert douglas.answer() == 42
Run the test as. The test was successful! !! This is a joyous discovery of the century (exaggerated), and everyone is happy to feel happy for the time being.
The state has changed from Red to Green, so next is Refactoring.
For the time being, let's make it easy to delete the comments that are from the beginning. And I also cleaned up the import area of the test code.
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_life_the_universe_and_everything():
douglas = FizzBuzz()
assert douglas.answer() == 42
The program has been refactored a little. Now that we have rewritten the program, let's test it. It would be a problem if the tests that passed were not passed. You have successfully passed the test code.
Now that I understand the flow of TDD, I will proceed with FizzBuzz. First, let's break down the FizzBuzz requirements into appropriate particles.
To think about the smallest FizzBuzz scenario, first identify the elements needed for FizzBuzz.
--When the argument is 1, the return value is 1. --When the argument is 2, the return value is 2. --When the argument is 3, the return value is Fizz --When the argument is 5, the return value is Buzz --When the argument is 6, the return value is Fizz --When the argument is 10, the return value is Buzz --When the argument is 15, the return value is FizzBuzz --When the argument is 30, the return value is FizzBuzz
Is it something like this? For the smallest scenario, this time, the top "return value is 1 when the argument is 1".
Originally, we should separate and identify the requirements in this way, but since it was the first time, we omitted it.
TDD first writes test code. For the method that does fizzbuzz, let's test the return value for the argument with fizzbuzz.
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_life_the_universe_and_everything():
douglas = FizzBuzz()
assert douglas.answer() == 42
def test_When the argument is 1, the return value is 1.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(1) == "1"
When I run the test, there is no method for fizzbuzz! I'm angry, but I'm looking forward to getting angry and press the test.
It happened. As expected.
Now let's implement fizzbuzz.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(i):
return "1"
This will pass the test! Alright, it's a test! !!
Well, I got an error saying that the number of arguments is strange.
Let's go around.
Apparently, you have to hand over yourself. It seems to be customarily named self.
Now let's modify the program.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, i):
return '1'
If you look closely, you can see that the answer argument also has self. Following this, self was given as an argument.
Oh, this passed the test.
You have successfully cleared "Return value is 1 when the argument is 1". Refactoring is a mixture of double and single quotes, so let's unify it to single quotes.
Now that the minimum scenario has been successfully cleared, it's delivered! !!
No, well, I've only tested at 1, so I think it's okay, but let's do a test at 2, so let's test at 2.
As before, write the test for case 2.
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_life_the_universe_and_everything():
douglas = FizzBuzz()
assert douglas.answer() == 42
def test_When the argument is 1, the return value is 1.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(1) == '1'
def test_When the argument is 2, the return value is 2.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(2) == '2'
Something like this. Added a test to return 2 when 2. Alright, let's test.
It's an error. I'm glad I didn't deliver it as it is ...
The error content is that I want 2 but I don't want to move in.
I was troubled. What to do now? A wise programmer said here. "Let's return the argument as it is" The typist rewrites the code as he is told.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, i):
return i
Oops? I got two errors. Since I was checking the match between the character string and the number, I will convert the argument to a character string and return it for the time being.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, i):
return str(i)
Let's test it.
The test was successful. Let's be very happy.
--When the argument is 1, the return value is 1. --When the argument is 2, the return value is 2.
I was able to clear the two items.
It's refactoring.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
return str(arg)
The argument i is confusing, so let's call it arg.
Now it's time to write a test and check for errors.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg == 3):
return 'Fizz'
return str(arg)
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_life_the_universe_and_everything():
douglas = FizzBuzz()
assert douglas.answer() == 42
def test_When the argument is 1, the return value is 1.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(1) == '1'
def test_When the argument is 2, the return value is 2.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(2) == '2'
def test_When the argument is 3, the return value is Fizz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(3) == 'Fizz'
This worked fine in case 3.
Now let's check the specifications.
readme.txt
Write a program that prints the numbers from 1 to 100.
But for multiples of three print "Fizz" instead of the
number and for the multiples of five print "Buzz". For
numbers which are multiples of both three and five
print "FizzBuzz".
Sample output:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
... etc up to 100
Even in the case of 6, if Fizz is displayed, it is written in the specifications.
Let's create and test a test when the argument is 6 and the return value is Fizz.
6 is the return value. Do not do that.
Consider the specifications.
It turns out that I want to display Fizz when it is divisible by 3.
And Mob says, "Please rewrite the if statement so that it is displayed as Fizz when the remainder of 3 is 0." Typists implement like machines.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg % 3 == 0):
return 'Fizz'
return str(arg)
It has been rewritten on the condition of the remainder.
I was able to safely display fizz when it was a multiple of 3.
Let's take a look at the current test code.
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_life_the_universe_and_everything():
douglas = FizzBuzz()
assert douglas.answer() == 42
def test_When the argument is 1, the return value is 1.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(1) == '1'
def test_When the argument is 2, the return value is 2.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(2) == '2'
def test_When the argument is 3, the return value is Fizz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(3) == 'Fizz'
def test_When the argument is 6, the return value is Fizz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(6) == 'Fizz'
Do you need so much testing? In this refactoring, we will remove unnecessary tests.
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_When the argument is 2, the return value is 2.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(2) == '2'
def test_When the argument is 6, the return value is Fizz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(6) == 'Fizz'
As long as you rub these two things, you can understand that you can process in multiples instead of fixed values.
Removing unnecessary test cases is also a good refactoring.
Since I checked the specifications earlier, it is obvious that this is also a multiple of 5, so I will write the test code for cases 5 and 10 and implement it.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg % 3 == 0):
return 'Fizz'
if (arg % 5 == 0):
return 'Buzz'
return str(arg)
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_When the argument is 2, the return value is 2.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(2) == '2'
def test_When the argument is 6, the return value is Fizz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(6) == 'Fizz'
def test_When the argument is 5, the return value is Buzz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(5) == 'Buzz'
def test_When the argument is 10, the return value is Buzz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(10) == 'buzz'
Oops error, this is the wrong test case. Let's fix it.
Let's delete the unnecessary test code h. Delete 5 cases.
Since I checked the specifications earlier, it is obvious that this is also a multiple of 15, so I will write the test code for cases 15 and 30 and implement it.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg % 3 == 0):
return 'Fizz'
if (arg % 5 == 0):
return 'Buzz'
if (arg % 15 == 0):
return 'FizzBuzz'
return str(arg)
test_fizzbuzz.py
from fizzbuzz import FizzBuzz
def test_When the argument is 2, the return value is 2.():
douglas = FizzBuzz()
assert douglas.fizzbuzz(2) == '2'
def test_When the argument is 6, the return value is Fizz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(6) == 'Fizz'
def test_When the argument is 10, the return value is Buzz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(10) == 'Buzz'
def test_When the argument is 15, the return value is FizzBuzz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(15) == 'FizzBuzz'
def test_When the argument is 30, the return value is FizzBuzz():
douglas = FizzBuzz()
assert douglas.fizzbuzz(30) == 'FizzBuzz'
It seems that it is displayed as Fizz. It seems that multiples of 3 have reacted first. That's also the case, because we check in the order of checking multiples of 3, checking multiples of 5, and checking multiples of 15.
Let's change the order.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
if (arg % 3 == 0):
return 'Fizz'
if (arg % 5 == 0):
return 'Buzz'
return str(arg)
It worked, and you can now deliver it. The scene is a big cheer.
Now for the final refactoring.
It's unpleasant to have if statements lined up, so use else etc. firmly.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
else if (arg % 3 == 0):
return 'Fizz'
else if (arg % 5 == 0):
return 'Buzz'
return str(arg)
Apparently else if is useless, python seems to conditional branch with elif. By the way, let's put the case that outputs numbers in else
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else
return str(arg)
I will test it. With this, ...
I didn't. .. .. ..
You completely forgot the colon.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else:
return str(arg)
This time it's done.
It's finally delivered! Applause It will be a drinking party today. I don't have a drinking party.
The overall flow of TDD when I first tried TDD with MobPro and the details of the failure were just as they were (not so much).
Like this
I'm going through the cycle of TDD, but by doing it with MobPro, I was able to do it with a slightly different feeling and it was very interesting. The result of the test was easy to understand, I tried running it for the time being, and when I got angry, I always googled and then wrote the code, so it was very refreshing.
Mob Pro, let's do it
** Last and last refactoring **
Someone said. "The language I use has a ternary operator, but what about python?"
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz_bu(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else:
return str(arg)
def fizzbuzz (self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else:
return str(arg)
For the time being, I will rewrite it to the ternary operator version while leaving the completed program.
First, there was a directive to replace only the case of multiples of 5 with the ternary operator.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz_bu(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else:
return str(arg)
def fizzbuzz (self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
arg = 'Buzz' if arg % 5 == 0 else arg
return str(arg)
You can confirm that the test has passed, so the refactoring is successful. Let's replace another case,
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz_bu(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else:
return str(arg)
def fizzbuzz (self, arg):
arg = 'FizzBuzz' if arg % 15 == 0 else arg
arg = 'Fizz' if arg % 3 == 0 else arg
arg = 'Buzz' if arg % 5 == 0 else arg
return str(arg)
I got a lot of errors. That is also natural. I'm trying to get the remainder of the string.
So what about a single ternary operator? So I tried to erase the contents of else and the substitution.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz_bu(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else:
return str(arg)
def fizzbuzz (self, arg):
arg = 'FizzBuzz' if arg % 15 == 0 else
'Fizz' if arg % 3 == 0 else
'Buzz' if arg % 5 == 0 else
return str(arg)
I will test while saying "This will work !!!".
Do not do that.
I wonder if line breaks are not good, and when I went through how to divide a long sentence program into multiple lines, there was a voice saying that I could go with a backslash, so I will add it.
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz_bu(self, arg):
if (arg % 15 == 0):
return 'FizzBuzz'
elif (arg % 3 == 0):
return 'Fizz'
elif (arg % 5 == 0):
return 'Buzz'
else:
return str(arg)
def fizzbuzz (self, arg):
arg = 'FizzBuzz' if arg % 15 == 0 else \
'Fizz' if arg % 3 == 0 else \
'Buzz' if arg % 5 == 0 else \
arg
return str(arg)
Hooray! It's a success! !! Cheers will rise as soon as it is completed. Now that we have a ternary operator version, delete the backup and it's time to deliver! !! !! !! !! !! !!
fizzbuzz.py
class FizzBuzz:
def answer(self):
return 6 * 7
def fizzbuzz (self, arg):
arg = 'FizzBuzz' if arg % 15 == 0 else
'Fizz' if arg % 3 == 0 else
'Buzz' if arg % 5 == 0 else
return str(arg)
Recommended Posts