Some simple tips to start working with TDD
When I initially started coding, I felt TDD didn't work because I wouldn't be able to code the test if I didn't know what to do. After some time has passed, I've come to a different conclusion. When I'm not sure how to handle a problem, I find that creating the test ahead of time helps the most in figuring out how to solve the problem.
TDD stands for Test-Driven Development and means you put focus on test before the actual production code.
Three TDD laws govern the process you should follow when using TDD. Here they are:
These laws contain us in a loop where we can only focus on and change one thing at a time. This allows us to take small steps and build up your projects incrementally.
When working with TDD, we always take small steps to reach a greater goal. To show you how I would've done a code using TDD, we will code the following problem:
Ok, now that we know the rules, we can start developing our code. Since we are using TDD, the first step is to create a test.
I'm assuming you all can create a project and configure jest or whatever test library you like (if you don't know how to do this, I think this is your first step, google can help a lot with this. Don't worry, I'll be waiting here)
Since we are beginning our code from the test code, is easier to start thinking about the interface that we will be using. In this case, we know that we need the enter and leave hours. So we need to pass this information to the function.
Since we are using typescript, we can use the Date
object to pass this info.
So for the first test, we will have something like this:
1test("should return the correct price", () => { 2 const enterDate = new Date('2022-03-27T10:00:00') 3 const leaveDate = new Date('2022-03-27T12:00:00') 4 5 const price = calculateParkingTicket(enterDate, leaveDate) 6 7 expect(price).toBe(2) 8}) 9
Now that we have a failing test, we can finally start working on the actual function.
So following the TDD, we are creating the amount of code that is needed to the test pass:
1export const calculateParkingTicket = (enter: Date, leave: Date) => { 2 return 2 3} 4
Now we have a passing test!! We have the right amount of code to make your test pass, even though this code doesn't solve our real problem completely.
If the real problem is not solved yet, we will write a new test!!
1test("should return the correct price when 5 hours passed", () => { 2 const enterDate = new Date('2022-03-27T10:00:00') 3 const leaveDate = new Date('2022-03-27T15:00:00') 4 5 const price = calculateParkingTicket(enterDate, leaveDate) 6 7 expect(price).toBe(5) 8}) 9
Ok, now we can't only return the number 2 from our function. We need to develop a more complex solution.
1const PRICE_PER_HOUR = 1 2 3export const calculateParkingTicket = (enter: Date, leave: Date) => { 4 const diferencesInMs = (leave.getTime() - enter.getTime()) 5 const diferencesInHours = diferencesInMs / (1000 * 60 * 60) 6 return diferencesInHours * PRICE_PER_HOUR 7} 8
Looks better! But what will happen if a car stays parked for 2:30 hours? according to our rules, it should cost $3. So let's write a test for this case
1test("should return the correct price 2:30 hours passed", () => { 2 const enterDate = new Date('2022-03-27T10:00:00') 3 const leaveDate = new Date('2022-03-27T12:30:00') 4 5 const price = calculateParkingTicket(enterDate, leaveDate) 6 7 expect(price).toBe(3) 8}) 9
Now that we have a new test we can start to code!
1const PRICE_PER_HOUR = 1 2 3export const calculateParkingTicket = (enter: Date, leave: Date) => { 4 const diferencesInMs = (leave.getTime() - enter.getTime()) 5 const diferencesInHours = diferencesInMs / (1000 * 60 * 60) 6 const hoursToPay = Math.ceil(diferencesInHours) 7 return hoursToPay * PRICE_PER_HOUR 8} 9
So, this concludes our code. Every rule is taken into account and tested.
Now we are free to refactor the code as most as we want, with the certainty that we will not break any rule thanks to the tests!
We coded a basic example with TDD, and I think it demonstrates how to apply this technique correctly.
Working with TDD, in my opinion, has various advantages, such as the ability to take incremental steps, consider how you will interact with the code, and, of course, ensure that everything works correctly.
But, keep in mind that this approach may be hard to implement in the real world. Because not every piece of code is so trivial to test,
Along with TDD, it is critical to have a good architecture that enables the creation of testable code. Using patterns such as dependency injection can also aid in the creation of testable code.