Project Falcon
1… 2… Fizz! 4… Buzz!
Git Bashmkdir fizzbuzz && cd fizzbuzznpm init -ynpm i parcel-bundlernpm i -D jest babel-jest @babel/core @babel/preset-envsrc dir and a test dir: mkdir src testjs dir in both: mkdir {src,test}/jscode .package.jsonupdate the scripts section of your package.json file
{ ... "scripts": { "test": "jest test/js" }, ... }
import FizzBuzz from '../../src/js/FizzBuzz'; describe('fizzbuzz', () => { test('Should return 1', () => { // Arrange const underTest = new FizzBuzz(); // Act const actual = underTest.parse(1); // Assert expect(actual).toBe(1); }); });
So all of our failures at this point are compilation failures. To fix these we need to make the FizzBuzz.js file with the parse method.
So now we have the following object in src/js/FizzBuzz.js:
class FizzBuzz { parse(inputNumber) {} } export default FizzBuzz;
Now run your tests to see this one fail. In GitBash run npm test.
Now, we do the SIMPLEST thing to make the test pass. In this case...
class FizzBuzz { parse(inputNumber) { return 1; } } export default FizzBuzz;
Remember, never write more code than you need to make the test pass. Even if you know how the code may change in the future. Be creative with your tests and let them dictate how your code should work.
Now, run your test to see it passing!
Now that we have gotten to green in the red -> green -> refactor cycle, it's time to refactor! Remember, refactoring can take the form of properly naming things, deleting unnecessary code, removing duplication, or anything else that generally makes our code cleaner.
At this point we don't have any clean up to do though, so it's time to move back to red.
Now that we've got a passing test for 1, let's write a test for 2.
test('Should return 2', () => { // Arrange const underTest = new FizzBuzz(); // Act const actual = underTest.parse(2); // Assert expect(actual).toBe(2); });
Run this test to see it fail.
So let's change our class under test to make this test pass...
class FizzBuzz { parse(inputNumber) { if (inputNumber === 1) { return 1; } return 2; } } export default FizzBuzz;
Remember, since return is a break statement, we will never run the risk of returning two outcomes from the same method. Run your tests to see them pass.
Time to make our code cleaner again. Now let's take a look at the logic of our method and think about how we can make it cleaner. At this point it's pretty clear that we are just returning the number we pass in. Let's make our method do that instead...
class FizzBuzz { parse(inputNumber) { return inputNumber; } } export default FizzBuzz;
Time for our third test!
test('Should return Fizz', () => { // Arrange const underTest = new FizzBuzz(); // Act const actual = underTest.parse(3); // Assert expect(actual).toBe('Fizz'); });
Run this to see the fail.
Remember to do the simplest thing to make the test pass.
class FizzBuzz { parse(inputNumber) { if (inputNumber === 3) { return 'Fizz'; } return inputNumber; } } export default FizzBuzz;
Since there really isn't anything to refactor, it's time to turn red again! So, for our next test, we are going to choose 5.
test('Should return Buzz', () => { // Arrange const underTest = new FizzBuzz(); // Act const actual = underTest.parse(5); // Assert expect(actual).toBe('Buzz'); });
Why 5 and not 4?
class FizzBuzz { parse(inputNumber) { if (inputNumber === 5) { return 'Buzz'; } if (inputNumber === 3) { return 'Fizz'; } return inputNumber; } } export default FizzBuzz;
We now have a passing test for 5!
Time to continue on our testing journey...
test('Should return Fizz', () => { // Arrange const underTest = new FizzBuzz(); // Act const actual = underTest.parse(6); // Assert expect(actual).toBe('Fizz'); });
This gets us failing. Let continue on the simplest road to passing.
Wait...
Now we have two tests with the same description ('Should return Fizz'). We can use a nested describe block to help with that. This is what we should have now:
describe("fizzbuzz", () => { ... describe("Should return Fizz", () => { test("for 3", () => { ... }); test("for 6", () => { ... }); }); });
Now we can nest anything that should return us "Fizz" in here so we're organized.
class FizzBuzz { parse(inputNumber) { if (inputNumber === 5) { return 'Buzz'; } if (inputNumber === 3 || inputNumber === 6) { return 'Fizz'; } return inputNumber; } } export default FizzBuzz;
Now let's examine our parse method and see how we can make this logic a little more clear
class FizzBuzz { parse(inputNumber) { if (inputNumber === 5) { return 'Buzz'; } if (inputNumber % 3 === 0) { return 'Fizz'; } return inputNumber; } } export default FizzBuzz;
So now we're using the modulus operator to see if the number we have is evenly divisible by 3. We do this by checking to see if zero is the remainder when we divide by 3.
Time to continue using this same logic to figure out the remaining numbers to test! Which numbers make sense to test? Which do we have enough confidence to skip at this point?