🎙 Develpreneur Podcast Episode

Audio + transcript

test-driven development

In this episode, we continue our season on object-oriented programming and explore test-driven development. We discuss how test-driven development can improve user-friendlyness and help with object-oriented design. We also talk about the importance of unit tests and how they should cover all methods in a class.

2021-04-07 •Season 14 • Episode 484 •test-driven development •Podcast

Summary

In this episode, we continue our season on object-oriented programming and explore test-driven development. We discuss how test-driven development can improve user-friendlyness and help with object-oriented design. We also talk about the importance of unit tests and how they should cover all methods in a class.

Detailed Notes

In this episode, we continue our season on object-oriented programming and explore test-driven development. We discuss how test-driven development can improve user-friendlyness and help with object-oriented design by focusing on unit tests and feedback mechanisms. The host emphasizes the importance of unit tests and how they should cover all methods in a class. We also talk about the importance of thinking about what could break or what could be done differently. The episode includes a challenge for listeners to try test-driven development.

Highlights

  • Test-driven development can help with object-oriented design and improve user-friendlyness.
  • Unit tests should cover all methods in a class.
  • Test-driven development involves pairing requirements with validations and tests.
  • It's essential to think about what could break or what could be done differently.
  • Test-driven development highlights ways to approach feedback mechanisms within classes.

Key Takeaways

  • Test-driven development improves object-oriented design.
  • Unit tests should cover all methods in a class.
  • Test-driven development focuses on feedback mechanisms.
  • It's essential to think about what could break or what could be done differently.
  • Test-driven development highlights ways to approach feedback mechanisms within classes.

Practical Lessons

  • Implement unit tests for all methods in a class.
  • Focus on feedback mechanisms within classes.
  • Think about what could break or what could be done differently.

Strong Lines

  • Test-driven development can help with object-oriented design.
  • Unit tests should cover all methods in a class.
  • Test-driven development involves pairing requirements with validations and tests.

Blog Post Angles

  • How test-driven development improves object-oriented design.
  • The importance of unit tests in test-driven development.
  • Test-driven development as a feedback mechanism within classes.
  • The challenge of implementing test-driven development in a class.
  • The benefits of using test-driven development in object-oriented design.

Keywords

  • test-driven development
  • object-oriented programming
  • unit tests
  • feedback mechanisms
  • design patterns
Transcript Text
This is building better developers, the developer podcast. We will accomplish our goals through sharing experience, improving tech skills, increasing business knowledge and embracing life. Let's dive into the next episode. Hello and welcome back. We're continuing our season looking at object oriented programming, practical side of it. And we are continuing right now talking about testing. This episode we want to focus on test driven development and how it can help with your object oriented design. It can help you build a solution that is much more, I will say user friendly to the developers that take advantage of it, whether that's maybe you or maybe some of your other team members. The focus is on unit tests essentially and really because these should be standalone objects or standalone classes to some extent, if it's truly object oriented. We should be able to do unit testing that is covering all the methods that we have within our class and hopefully within those that we're able to test the core functionality of it. This is, therein lies the rub as they say. When we're building a good interface in our class, there may very well be some things that are done under the covers. Things that are calculated, are manipulated and all of that goes on underneath and doesn't necessarily show up in the methods or exceptions or things like that. That may be good because we want to hide our implementation. That's part of the object orientedness. We want to be able to protect that so that if we change our implementation that we can do so without impacting the developers that use it. However, there are going to be situations where maybe things break in the middle of a process. You've got a method on your class that under the covers has 10 different steps that it does. Generally, that's fine. It doesn't matter how many steps really. As long as it comes out correct, then we're okay. Let's say that there's, whether you break on or fail on step three or step six or step nine, those are very different reasons for the failure. They're very different remediations or fixes that would need to be done in order for that to successfully go through all of that, basically complete successfully. That's going to highlight to you, since I have three different messages I need to give to the developer, depending on what situation I am. That's essentially three different either error codes or exception types that I need to be able to raise in order for the developer to know what's going on. This goes back to what all of us complain about probably as developers. The situation where something breaks and that's all we know about it. The code doesn't run. It doesn't tell us anything useful. Doesn't give us any meaningful feedback. It just says, no, it didn't work. That's the most frustrating thing to deal with. It's broken. I have no idea where to begin to look at why it's broken. With a class, it's even more so. They may not be able to crack open our class, look into our code, and try to figure out what's going on. They may have nothing at all that we're giving them. They may have nothing to work with other than just keep trying different values and trial and error, try to figure it out. We, I think, all know that that is the most slow, painful, and tedious way to debug anything. This is where test-driven development comes in very handy. As we're going through, and I guess let me step back just in case you haven't really been exposed to test-driven development. The idea, the process that is done in this case is that you look at the requirements for whatever your application is. As you walk through the requirements, you're going to essentially pair those up with validations, with tests that you could run to say, if this test is successful, then this requirement is met. For example, if the requirement is, I need to be able to take two numbers and add them together, then part of the testing side of that requirement would be, there should be a test where I can give two numbers to it and I get the solution back. If I give two plus three, if it's an addition, then I should get five back. Within that, we also need to be thinking about what could break or what could we do that may be not exactly, raise an exception, or maybe not exactly correct. We would look at things like within that test-driven development, maybe we start with just the addition one. Maybe we give it two positive numbers, five plus three. Maybe we give it a positive and a negative, so four plus negative eight. Maybe we give it a zero, eight plus zero. Maybe we give it, just in this case, we'll give it two negatives, minus three plus minus three. All of those are going to be things we're going to test. Then we're also probably going to test some things like, well, what if I only give it one number? What if I give it a number and a letter? What if I give it two letters? What if I don't give it anything? Some of these are not going to be possible. For example, if you've got a method that has two parameters, you're not going to be able to give it one parameter in most cases and languages because the language is not going to compile. It's going to say, hey, wait a minute, you can't do this. They don't always check the validity. Let's say that your parameters are two different instances or two objects in themselves. What if the object's null? What if an object's not in a valid state? Things like that. What if there are values that are outside of the limits? The most common would be, let's say that you've got something that only works if the parameter you give it is, let's say it's a rating. It's a letter grade. I've got a method and I say, I'm going to give you a number and then it's going to give me back a letter grade. Well, the number is only 0 to 100. That's the only thing that I'm working with. Let's say it's got to be an integer. That's inclusive 0 to 100 because you can only get no worse than 0% and no better than 100% on a grade. Well, in that case, you're going to want to test things. I'm going to send a 0. I'm going to send 100. I'm going to send each of the letter types, a grade that should fall within each of the letter types. I'm going to try things that are not. I'm going to try a negative 5. I'm going to try 108. What happens in those cases? Make sure that the requirements cover that. Because there could be situations where if it's higher than 100, then it's just automatically an A. If it's lower than a 0, it's automatically an F. I don't know how you'd have that lower than a 0, but hey, let's say that we allow for that. That requirement and then the related tests that you're walking through to say, well, what are the various ways that this can work or not work are key because then since we've basically put together the test case, we haven't actually built the test. We've just talked about the test cases. Let's go to that grade thing. This means because I need to handle less than 0, I need to do something when I'm given a score less than 0. I need to do something when I'm given a score greater than 100. And then obviously 0 to 100 inclusive. Then when I build out my unit tests, I should be able to test each of those. And the result, the response should be what's expected. So if I give it a negative 3, the requirement's going to tell me what happens. It may say that if the score is below 0, then we need to warn the user that it's invalid. If it's over 100, then we need to tell them that it's invalid and things like that. Well, in so doing, that means that we have to support that. So now our grade can't just be returning a letter because what happens if they give us something that fails? We can't just give them a letter. We can't just give them no letter. We have to have some way to communicate that an error occurred. Now in this case, it may be an exception. Or it could be that what they get back is either a single character, A, B, C, D, F, or they get a string message. If it's something where if it's longer than that one character, then they know it's a message they need to display. Now that's a little bit hokey. I guess not hokey, but a little kludgy because in that case, that means the user has to actually check the value and do their validation on their end to see what was returned versus us being able to have something that, and maybe it stores a value so they can check a return code, but something that doesn't force them to do something sort of additional work like, okay, is the thing that they returned only one, a length of one, or is it greater? And in which case, if it's greater, I've got to assume it's an error. It's things like that. Maybe you return something that's more complex than a value. This is actually fairly common and very useful if you're doing this kind of test-driven development is you've got some sort of an error class or something that you can populate. What you do is you just because of your system, you always have a, and maybe it is, maybe it's like a global static type thing, is you have a latest error message. So if whatever the latest call was, they can call it and then go back and take a look at the return code. Was it successful or was it an error code? Is there a message or not? Or maybe you send something back that's a value that also includes a response. Somewhat like, that's really like, sort of like the web does. You get a response code, maybe a 200, a 400, a 401, 500, whatever, and you get the response text. These sorts of things, when you do the test-driven development, when you start looking at what are the tests and the validations I'm going to need to do for my system, those are going to highlight ways that you may want to approach the feedback mechanisms within your classes. What am I really going to return? Can I just return a value? Do I need to do something bigger? Are there a series or a family of exceptions that I need to be able to, I need to make sure that they're created and they're supported and documented as part of my solution. Which, of course, as we move forward from test-driven development, leaves us into what's the next thing that we're going to talk about is the documentation side of our object oriented design. That being said, I think we're at a good point right here to throw you that challenge of the day or the week. We talked last episode, we asked, the challenge was take a look at the unit tests that you're doing. I would say at this point, take something that you're working on and try, just go through the exercise of test-driven development. This can be, it's been 10, 15 minutes, take a look at maybe it's just a couple of requirements, but think through, based on those requirements, the full requirement. This is what's expected, these are the exceptions, these are the ways that a user needs to be notified. Walking through those, what are the tests that need to be, that would make sense to be created for that, the expected values, and then does your solution, does your software support that? Is there a way for a user, a developer calling that to get all of the various different messages that are expected to be supported as part of the requirement? If not, you probably need to adjust your interface to that a little bit. That being said, hopefully you don't have any of those kinds of worries and you're writing perfectly awesome code like all of us do all the time. If you're that one person that occasionally writes bugs, we apologize, we hate it for you because the rest of us don't have that problem. That's total sarcasm for those who don't get it. We all do this, that's why we test, and we test early and often if possible. As we do that, let's get back to the programming side of our day, the work side of our day. As always, go out there and have yourself a great day, a great week, and we will talk to you next. One more thing before you go, the Developineur podcast and site are a labor of love. We enjoy whatever we do trying to help developers become better. But if you've gotten some value out of this and you'd like to help us, it'd be great if you go out to developineur.com slash donate and donate whatever feels good for you. If you get a lot of value, a lot. If you don't get a lot of value, even a little would be awesome. In any case, we will thank you and maybe I'll make you feel just a little bit warmer as well. Now you can go back and have yourself a great day.