Lesson 1 of 15

Day 1 Intro

Duration: 48:04 Watch on YouTube (external link, opens in new tab)

Markdown version

What does the "Unit" in Unit Testing (external link, opens in new tab) mean? Learn about the three end results (external link, opens in new tab) of a "Unit" and how they relate to the three types of tests: Value Tests, State Tests and Interaction Tests (external link, opens in new tab) with Mock Objects (external link, opens in new tab). Then we will jump into the basics of XUnit Frameworks (external link, opens in new tab).

In this lesson

Roy Osherove describes test-driven development (external link, opens in new tab) as writing code as if the code you'd like to have already exists, a personal practice that was declared officially in extreme programming. He defines a unit as a unit of work (external link, opens in new tab) that starts at a public API and ends in a return value, a state change (external link, opens in new tab) noticeable at the same level of the API, or a call to a third party (external link, opens in new tab), the only place for mock objects (external link, opens in new tab). He questions claims that unit tests (external link, opens in new tab) make bugs easier to find and code easier to maintain and understand, then introduces xUnit frameworks (external link, opens in new tab) and their arrange, act, assert (external link, opens in new tab) structure.

Transcript

7,166 words

[00:05] (external link, opens in new tab) So what this course is going to be about is not only about how to do test-driven development (external link, opens in new tab) but also how to do unit testing (external link, opens in new tab) the difference between integration testing (external link, opens in new tab) and unit testing. What's the difference between behavior driven and test driven development so that we get to know all the differences and choose what we want to work with. Now on the first day we usually focus on the unit tests on just writing clear maintainable (external link, opens in new tab) readable and trustworthy (external link, opens in new tab) unit tests.

[00:37] (external link, opens in new tab) We focus on test-driven development and that technique. We'll get to work with the string calculator (external link, opens in new tab) TDD kata. Some of you might know, some of you might. On the second day, we'll dive a bit more into the idea of mock objects (external link, opens in new tab) and stubs (external link, opens in new tab) and we'll look at different frameworks. We'll take a look at RSpec mocks. We'll take a look at the framework called Mocha and we will be using a framework called Bogus.

[01:07] (external link, opens in new tab) Has anyone heard of this? Yeah. It's relatively unknown but it's we'll try to compare the different framework and understand why some of them are actually in my mind are better than others and why some of them will actually cost you time and money during development and some won't u or some will cost less. Now on the third day, we'll continue to work with the these mock mocking and isolation frameworks (external link, opens in new tab). I don't call them mocking framework.

[01:38] (external link, opens in new tab) We'll talk about that of course. And we'll also start delving into reviewing and best practices in unit tests. We'll talk about integration tests and we'll talk about start talking about legacy code (external link, opens in new tab). On the fourth, they will do legacy code and will write tests for existing existing applications either to for your code or for other people's code. If your code is very very complicated, we'll start with other people's code that's simpler and then we'll build up the u skills and then on the fifth day we'll definitely write tests for your so whatever happens on the fifth day we'll try to write some tests for your code or to separate the logic and write tests for that only or we'll see some approaches.

[02:23] (external link, opens in new tab) Now if you want to we can devote some of the third day to building that pipeline we talked about before. Which is the shipping skeleton (external link, opens in new tab). So if you'd like we can actually we can actually have a whole section about if you're interested. Okay. Are you interested? Okay. So we'll do that. So we'll talk about TeamCity, we'll talk about u source control check-ins, triggers, different build configuration types.

[02:54] (external link, opens in new tab) And just so you know this is this is a big subject so we can cover it. I usually teach a course about it and it takes two days just about this. I call it beautiful builds (external link, opens in new tab). We can definitely take like maybe half a day and just devote to that subject as well because it's very important. So we'll start with that on the third day. But first we'll talk about the unit testing. Okay. Okay. So I actually started talking before about a test-driven approach to life

[03:27] (external link, opens in new tab) Life to development life which is that whenever I start to develop something I start by expecting something big to happen but when I'm saying expecting is that I'm typing some kind of expectation about something that's supposed to happen and then I might expect many smaller things. When I say test-driven approach to life, it means that I'm looking at the world, but I'm seeing a different world. I'm seeing a world where some things already exist that aren't there yet.

[04:00] (external link, opens in new tab) So, when we're talking about test-driven development, we write code as if the code we'd like to have already exist. It's a very different mindset than writing code and writing tests after the code. So, it it takes a bit of getting used to. So now in real life we usually start with an acceptance test (external link, opens in new tab) either with things like cucumber or things like Capybara or other things that would test a highle feature set of our code. So we might automate the browser we might use nodejs to to automate browser without a browser.

[04:35] (external link, opens in new tab) We might use TCP web stacks to to automate network features and then we would fill in the gaps of that feature not working by incrementally working towards that feature. Now test-driven development is definitely not a new thing. I know that people in Ruby have been doing it for a long time or at least saying that they're doing it for a long time. And this is my impression of the Ruby world is that everyone in Ruby talks about testing and unit testing and TDD.

[05:11] (external link, opens in new tab) But maybe 20% of the people actually do it. This is very different from let's say the .NET world where 20% of the people are talking about TDD and unit testing and 20% of those are actually doing it. Okay. So there is a much bigger understanding that unit testing and TDD is very important. But there is there is a big lack of understanding of how to make those tests actually helpful. So a lot of people try to do testing in the Ruby world but they fail and then they give up and then they stop doing it.

[05:48] (external link, opens in new tab) So we'll talk about when that fails as well. Now TDD actually started u in in was declared officially in a in a methodology called extreme programming. Has anyone heard of extreme programming? Has anyone heard of scrum? Okay. Extreme programming is to scrum like I don't have a good analogy for that but extreme programming is a very technical view of the world.

[06:20] (external link, opens in new tab) And it talks about all the developer practice. Scrum is a process way of working and it doesn't talk about anything that the developer does. It only talks about iterations and demos but it leaves this big black hole of what happens inside an iteration. So extreme programming you can think of it as how would you would actually work on a day-to-day basis and scrum is how you how you would work on a higher level. So, one of the reasons that people fail in scrum is because they don't have unit test and they don't do test-driven development because scrum for example is about showing demos all the time at least every two weeks but most code

[07:01] (external link, opens in new tab) To be able to release it is very complicated. And so and just fixing the bugs and getting the features that were done in the iteration at the end of the iteration it takes so long to do that scrum fails that you can't even do the demos correctly because just releasing the code is an act of god or something. So extreme programming is where we're drawing our inspiration from. However you can take testdriven development as a feature of working and put it in any framework in any methodology because it is a personal development activity.

[07:39] (external link, opens in new tab) It is between you and your computer just like debugging. No one tells you when to debug and hopefully no one should tell you when to do TDD. Okay? Because you will want to do it all the time. I'm telling you to do TDD, but no one should tell you for for example to stop. Okay? Because TDD is not a feature of an application. It's it's a it's a measure of professionalism.

[08:13] (external link, opens in new tab) Okay. So to to talk about test-driven development, we have to talk about unit testing and behavior driven development (external link, opens in new tab) and all that stuff first. So I want to start with talking about what does it mean to talk about a unit in unit testing. You've all heard the term unit testing, right? What does it mean to say unit in unit testing? What's a unit?

[08:46] (external link, opens in new tab) A function or a sub routine. Okay. What? Any other ideas? That's not a bad idea. I mean, I used to think that three years ago. >> Smallest. >> Mhm. a function or a method in a class maybe other ideas >> if you kind of divide it further.

[09:18] (external link, opens in new tab) >> Okay, so unit has to be the smallest type of thing in >> Okay. Do private methods count as a unit? Okay. Hi As Okay. So, we're all in agreement. I mean, I used to be in agreement with you up until 3 years ago.

[09:51] (external link, opens in new tab) And then something happened. A meteorite hit my head. I'm just kidding. Three three years ago, I I came to a realization that this doesn't work for me anymore. I used to feel that unit in unit testing is the smallest piece of code. So I would write tests for methods or private methods or anything really. But I realized that the tests that this was creating were very very brittle.

[10:23] (external link, opens in new tab) They would break on the simplest of changes. And I started doing less and less testing of private method. So I started only testing public method. So suddenly a unit for me didn't mean a a single method. You could mean many methods that start with the public method in the same class. But then some of the private methods were calling other classes. So I said okay I can create a mock object or a stub of those classes.

[10:56] (external link, opens in new tab) But that became very very cumbersome. It was really hard to write those kind of tests when a private method was using some web API and I had to fake (external link, opens in new tab) that that I had to do that. But when a private method was using some class that I owned, I wanted to fake it, but it was very difficult to do as well. And the test became very unreadable. And if it was all just in memory, I I start I stopped doing that as well. So if a method was using one of my classes in memory, it didn't touch the file system, it didn't touch the network, I just kept it as is because it's a private, it's a private use.

[11:28] (external link, opens in new tab) And then I just tested the public method. And suddenly a unit was not not necessarily multiple methods in the same class. Could be multiple methods in several classes. And that's where I am today is that a unit is a unit of work (external link, opens in new tab) really, but not a unit of code. It's a use case in the system. It's a something that starts with the public API. This is how I visualize it. >> Should I remove that? >> You're actually answering my question.

[12:04] (external link, opens in new tab) Which question am I answering? >> What's hard to test? >> Oh, okay. We're starting to touch on that subject. >> Private private methods change. Yeah. As long as doesn't matter. Yeah.

[12:38] (external link, opens in new tab) >> So, it seems like you agree with that. But let's dive in even deeper because there are other reasons why to agree, not just because private methods change. Okay. But I agree that the main thing that drove me is that method names kept changing or the implementation of methods kept changing. a method started g getting called twice instead of once and suddenly my test was failing because I had a fake for the method. So I have a public API and then the public API does something something I I don't care what it does inside but it eventually ends with one of three different end result (external link, opens in new tab).

[13:12] (external link, opens in new tab) It could end with all of them but there are only three types of end results that I could think of and they correlate to three types of tests that I would do. So the simplest test is when I invoke something and then I get back a value. That's called a return a return value test or you get back an exception or an error. So you would call you would add two numbers you get back a number. That's the simplest test in the world, right? And then you would have other types of tests when you have a method but the method doesn't return anything

[13:49] (external link, opens in new tab) But it definitely does something, right? You have methods like that that don't do anything that those methods change the state of the code or the application. So adding a user is a method that changes the state of the application. Right? Then you would have a different type of method. It doesn't return anything and it doesn't change the state of the application. Those are the most complicated types of tests. Those are the methods that are just forwarding to a third type to a third party like methods that do logging.

[14:22] (external link, opens in new tab) So forward it to the logging web API. Okay, forward it to Twitter. Those are the places only in that third part third type of end result where we call third parties. That's the only place where you would want to actually use things like mock objects. Okay, because the other two tests, you should be testing in a much simpler way. Now, when we talk about mock objects, mock objects are a very complicated thing. They complicate your tests in a way that makes it less readable.

[14:56] (external link, opens in new tab) Now, we're going to talk about frameworks that try to help, but it's still going to make them less bad, but they're still less good than tested without mock object. I hope I hope that makes sense. Here's the point. If you can avoid using mock objects, please do because your code will be simpler to write. However, if your test becomes very very slow or very hard to read, mock objects might actually be the solution. So maybe

[15:26] (external link, opens in new tab) 5% of my code uses mock object in my tests and the rest is using stubs. And now the difference between mock objects and stubs we'll see tomorrow is that I don't assert on stubs and I do assert on mock object. So I would check that something got called here a third party but I wouldn't check that the third party got called in these two things. I would just fake out the log so I don't have to worry about it and that's that's it. Stub it out.

[15:56] (external link, opens in new tab) But I would assert against a return value from the same method and here I would assert on something on the system. So let's talk about the differences now. when I when I when I have a test that invokes a method, it invokes a method and gets back a return value. So the assert is happening at the same level and on the same function that was invoked. Those are the simplest kind of tests.

[16:27] (external link, opens in new tab) Okay, an example of that invoke something, you get back very very simple. Just kept just like look let's just look at that for a second. Now the second type of of test is a state change. So some people call it statebased test. For example adding a user you would then should be you then should be able to test somehow that the user was added. Right?

[16:57] (external link, opens in new tab) So here's a question. If you want to add a user, how would you test that the user was added >> Okay. So, you would go to the database and do a query basically. Okay. And you were saying you just do a query of the counts of users.

[17:27] (external link, opens in new tab) >> Yeah. Okay. So I would avoid quering the database if I don't have to. In in fact what I would try to do is that I would assert at the same level where I invoke the method. So if I have a method that adds a user, an API in some level,

[18:07] (external link, opens in new tab) And test, but then wouldn't go to and would want the assert same level add because I don't want to worry about the implementation details of a database. Now that means that's probably an integration test for now unless I fake the database and make it a stub. But I still want to test at the public level of the same API that something worked that the state has actually changed. But why why do I want why not just go to the what's the point?

[18:38] (external link, opens in new tab) Any ideas? Here's an example. Sorry, what? It takes time. Okay, for avoid the fact that it takes time. Okay, let's say it's an in-memory database. Doesn't take any time. Okay, here's an example where that's not good. I go and I query in the database and the user was indeed added to the database. However, nothing is it was added to the the table that I'm quering is the wrong table.

[19:10] (external link, opens in new tab) So the users are actually gotten from a different make sense. So what that means is that the user is actually added but it's not able to log in. Make sense? So the feature doesn't work even though the user was added to the system. It was added but it's not used correctly. So the code that queries that the code that uses the users is using the wrong table or it's quering differently.

[19:43] (external link, opens in new tab) I don't know but some for some reason I add a user but the user never shows up for other users to log in. So that means that the feature doesn't actually work even though the unit has passed. So technically something is in the database. But the fact that something is in database doesn't mean that the user is actually being used. Make sense? Let me try to put it in a different way. A better test would that I add the user and then I try to log in

[20:22] (external link, opens in new tab) At the same level of the API. That's when the login works after adding a user. I don't care about database implementation and I know that the feature works. I can actually demo the featurery the database I might be lying to my because the fact that there is a shared resource doesn't mean that the shared resource is being used correctly on the other side. Okay. So a a write operation without a read operation doesn't mean anything because

[20:53] (external link, opens in new tab) A feature has to both write and read keyword. Make sense? in case of a database but it would work for anything. What other types of states do you have? Here's an example. You have a special calculator and the calculator has two states. It has like a broken state. It's a good state. Okay. Now in the broken state you can you you cannot do any calculation. In a good state you can do a okay now you set it to the broken state.

[21:27] (external link, opens in new tab) The bad analogy here would be that you go and you check an internal field or an attribute inside the class. It's boolean to say whether it's broken or not. But the much better test would say try to do a calculation. Okay, that means that the feature work of just the implementation. Make sense? So a state change has to be noticeable at the same level of the API that you

[21:59] (external link, opens in new tab) That's the guiding rule here. So for any any method that doesn't return anything it obviously changes something. So the the question is how do I notice that something same level maybe a different class and if I can't then why am I doing it right? So let's say that you write something in a database and no one ever uses that. So why are you doing it? That h that happens right?

[22:29] (external link, opens in new tab) In a project that I'm working on, we had this requirement to write something to the database. Whenever something happens, write something and notify and put a row in the database. And I said, okay, but and and we decid to do it as an integration test, which is a test. It's not you have shared resources. Sometimes you have things you do not control. But I was sitting with my parents said, "But okay, but how do we know? How do we test that it actually is written?"

[23:00] (external link, opens in new tab) My buddy said, "Hey, let's go to the database and check." Yeah, but is but what if we look for the thing that's actually using the row in the database? Said, "Is it our application that uses the the database table?" I said, "No, no, we're just writing to it and then a different application is using said, okay, which application is it like a reporting thing using it?" He said, "I I don't know." And we started looking and it's a big organization. So, and there are like 17 different projects. So, we go around. Nobody is using that those types that field that column in the table.

[23:35] (external link, opens in new tab) Nobody is using that. So we go to the product owner and we say hey you gave us a story where we need to notify this thing why because he said what do you mean why I need to know that it's done say then we asked how do you know that it's done how do because we write it to this table I said okay I just go manually to the table and I do my own query okay so the the way to know there is no application that uses

[24:06] (external link, opens in new tab) So the way that to know it works is to actually go and do a query in the application in the database. So that's how we actually eventually tested it because this is the the use case reading it. So that's how it's end up. But at the same time when we looked at the row we said yeah but these columns we've been writing them for three years and I'm I don't ever look at it's like wasting wasting. So this is a good example of having a test that's written in a good way actually makes you think about what feature is

[24:41] (external link, opens in new tab) And who's using it. What's the feature in the first place? So that writing that test actually made us do conversation about who's using something and why why do you are you even doing it? And that's also the point of private method because private methods they lead you if you think of a method as an entry a test every private method becomes an entry and then what happens you write a test for a private method and no one ever uses that private method.

[25:13] (external link, opens in new tab) What's the point of a private method that nobody calls? Can you tell me if a private method never gets called? If a private method falls in the forest and no public method ever calls it, we can actually delete it, right? But when you think about it so granularly, we don't even think about the fact that we can delete the private method's calling it. When we think about the public APIs, we think about the use cases. We think about the API that someone else will use and we think about noticeable state.

[25:44] (external link, opens in new tab) So we think about it from and that's okay. So, here's an example of a noticeable state change. You change the light, look at the system, system looks different. Okay, change the light. Look at the system. System looks not my kid, by the way. Just found it. Just so you know, no kids were harmed in making this. No, none of my kids were harmed in making this movie. But that's an example or you look at the same level of the API that you invoked and the system behaves differently.

[26:18] (external link, opens in new tab) That's what you want. Okay. Terrifying but true. Okay. A third type is the most complicated type is when you have a method that doesn't return a value, doesn't change the state, but calls a third party instead. So, we might have something calling Twitter, etc. And then the point is that we would have a third party call (external link, opens in new tab).

[26:50] (external link, opens in new tab) But where does the assert go? Can we assert on Twitter that Twitter got the message? Probably not. And if we did, it's going to take a long time. Although that would be the perfect test, right? Register on Twitter and then go and query the Twitter API that you your application has permissions to read for that user. That would be the perfect end to end test. And I would encourage you to try and do that, but not now because that's going to take a long time. You don't want to automate it too much. You don't want Twitter to ban you, etc.

[27:22] (external link, opens in new tab) So instead, you have to figure out a way. You can't actually assert on the third party. So instead, what you do is that you create a mock object, something that looks like the third party, and then the third party call on the unit that calls the third party calls the mock object instead of the third party. and then you assert on the mock object that it actually got. Those are the most complicated types of tests.

[27:52] (external link, opens in new tab) Here's an example of a mock object in real life. So you can actually record what users were typing on it and then you can take the credit card number, the the code numbers and then a force test that they work. No, this is this is real. You know that, right? So, gangs use this to to get all the illegal code. But this is the perfect example of a mock object that records the interactions. Then you can take it with you.

[28:22] (external link, opens in new tab) Now, so those are that's the point of unit and unit testing. But what's the point of unit testing in particular? Because when I talk to people and I ask what what does it mean unit testing for you? That's usually what they say. They say unit testing is great because it's easier to find bugs when you have unit test. Now the question is how many unit tests do you have? If I if I give you a method called let's say add a user and user has a string password and a username.

[28:54] (external link, opens in new tab) Okay. And it returns a boolean whether that user is valid. How many tests can you write for that map? The minimum. How many tests will you write for a method that validates user based on username and password? >> One. >> Okay. Sure. I mean, which one would you start with? If you only had one, which has the success one or the the failed one?

[29:27] (external link, opens in new tab) Yeah. How would you know it fails when it should? That's why you would write the second. So just two tests. What about nil users in password? User that exists in database, user that doesn't exist in database, user that exists with the wrong password. Okay, user that exists with an old password, etc. You could probably write two, three, four. Some people get up to seven, but I'm talking about the minimum. But if we say that for every method that has any kind of logic in our code

[30:01] (external link, opens in new tab) That is an entry point (external link, opens in new tab) method that has some logic inside it we have probably at least two or three tests. How many methods with logic do we have in our code? How many do you have in your current project? Methods that have logic like ifs, loops, stuff like that. Sorry. Yeah, lots is not a number. Give me a number at least. Is it more than 100

[30:42] (external link, opens in new tab) >> Let's say the last project you worked on. Let's not mention names. >> 50. Okay. 50 method with logic. So you would have at least how many tests? probably at least 100 >> realistic or no at least not realistic. >> Not per perfectly is not the minimum amount of tests. So a minimum is the realistic

[31:14] (external link, opens in new tab) Probably at least 100 150 right but most developers don't have that amount of type of code for but let's talk about why do people find that unit testing is actually helpful and the reason I'm saying this is because if you have let's say 150 test methods let's say you have a thousand some projects have a thousand or several thousands of test methods So if you have a thousand tests in your application, is it actually easier to find bugs?

[31:54] (external link, opens in new tab) Takes longer time, right? But that's not the question. Yeah, if the test caught bugs if you wrote them test driven, but that's not you wrote them after. And so the big question is the is the tests is this is not actually true. It can only be true if the tests do not have bugs themselves. Okay? Because how would you know that the test has a bug?

[32:25] (external link, opens in new tab) Would you write a test for your test? How would you know that the test for the test doesn't have a bug? It's a very recursive problem. So, one of the best ways to avoid bugs in the tests (external link, opens in new tab) is to use test-driven development because in TDD you start with a failing test. You probably heard this. You start with the failing test and then you make the test pass without touching the test, just touching production. And that's basically how you test the test (external link, opens in new tab) because you see that it fails when it should.

[32:57] (external link, opens in new tab) Then you make the production code changes and then suddenly the test passes. So you know it passes what it should and it fails. So testing the test this way is the almost the only way that I know of verifying that. So it is easier to find bugs if the tests don't have bugs. We'll talk about that. We'll do that today. It's easier to maintain your code if you have a thousand tests. Make sense?

[33:27] (external link, opens in new tab) Does it make sense? Maybe. Yes. Depends on the test. Sorry. What? >> It's easier to see if you break something. That's true. But that's about finding the bugs. Oh, but are you talking about that when you maintain when you change code, you see if you broke something? >> You make a new feature by doing that. Yeah. >> You broke some old part. >> Sure. Y

[33:58] (external link, opens in new tab) Test. >> Uhhuh. I agree with that. However, let's consider a simple scenario. You have a class that has an initial initialize method, a constructor that takes two arguments. Okay? And now you either remove or an argument or you add a new argument that's required. But you already have 50 tests that use the class with the two argument.

[34:28] (external link, opens in new tab) So now you have 50 tests that you have to change before you even start working. So suddenly maintainability becomes a nightmare, right? So that's that sucks. So actually I would say that the biggest contribution to failed tests in a project is the maintainability aspect because if you write tests in a non-maintainable way the tests will actually hurt you and take you much more than they save you because tests are supposed to save time debugging but they actually cost more time

[35:01] (external link, opens in new tab) Because of maintainability issue. So we'll talk about in this course how to make the test more maintainable. Is it easier to understand your code when you have a thousand tests? Some people say that's true. Do you think it's true? No. Why not? Just because I said no for the other things doesn't mean that it's wrong now.

[35:44] (external link, opens in new tab) True. So it's harder to to read tests because they're not written in natural language. But if you think about it, I'm not talking even talking about product owner stuff like things like cucumber. I'm talking about tests as documentation for our code that can be read read almost like a book. We we're going to learn how to write tests that can be read almost like a book. And we'll see that some tests can and can never be read like and you have to use a different framework for that and you have to use different structures. But only when all these things are true,

[36:15] (external link, opens in new tab) All the three top things are true, then it is truly easier to develop. But if you just remove even one of them, you make the test actually harder to find bugs when tests have bugs. If you're testing the wrong thing, the test passes, but the bug is still there. Right? If it's harder to maintain, it's harder to understand your code because of the test, then actually the development experience becomes harder because of the test instead of easier and so all the investment is gone. In one of the f first project that I ever did unit testing in, I was the team leader, I decided we're going to do this test driven.

[36:50] (external link, opens in new tab) I've never done this before. So, we wrote really bad test. It took us about four months or five months to start feeling the effects. What happened is that we started changing the code and the test just started breaking like crazy and at some point you say you know what I don't have time for this crap I just want to get things done so we started removing tests and we worked so hard to write those all the time was just wasted and we started debugging more because we had less tests we didn't want to write more tests because we understood that causes more pain so we got into a very negative loop

[37:32] (external link, opens in new tab) And that's the kind of thing that can happen if you don't worry about the maintainability and the readability (external link, opens in new tab) of the code. So all that money goes to waste all the time, all the blood and tears and and sweat. However, in that same project, we had a few integration tests that even though we had that massive bad experience, we still had some tests that really helped us because we had a good experience with with database changes. We had a lot a lot of integration tests. We didn't know the difference between unit and integration.

[38:02] (external link, opens in new tab) This was maybe 10 years ago. And we wrote some tests that make sure that things are going in and out of the database with the with the framework. And then one day maybe eight months into the project before delivery we did have some tests passing and those were the tests the integration test the customer decided that the database schema had to change. Now you know that you change database schema eight months into the project.

[38:36] (external link, opens in new tab) It's not good. However, they need to change nothing except just a couple of primary keys and add to them some things and you know that's great. So luckily we did have the test. We ran the test before they were passing. We added the one primary key extra. Some tests were failing. We fixed them. Then the test then moved on to the next one. And within a day we made all those changes work. So some tests were really helpful. Some tests were just horrible.

[39:08] (external link, opens in new tab) So you can have that mixed experience as well. We still that project still failed by the way. Okay. But it failed for a completely different reason. It failed because this was an eight-month project and we committed to three months. We we failed on features, time, and quality. No, not on quality. Sorry, on quality. We didn't fail, but features and time we failed because when I was a team leader, my project manager came to me and says, "Roy, we need you to do this in three months." And I said, "It's going to take eight months." And he said, "No, we I know you can do it in three months." And what did I say?

[39:44] (external link, opens in new tab) Okay. I said, "Okay." How could I say, "Okay." It wasn't reality. I just I just was, you know, developers, we're so optimistic. Maybe if we work really hard, we can do it. But was it was still took eight months. So now I I felt like a liar. Not just bad for the project, also bad for myself, which is another reason. Anyway, we're going to talk about a lot about mistakes in this questions about unit testing. So far, so far it's pretty easy, right?

[40:19] (external link, opens in new tab) Now, when I teach this course to people who have never done unit testing, I tell them that they're wrong because all developers have always done unit testing. The only difference is that they haven't done it in a structured way because I don't know a single developer who doesn't check their code and check the application before they hand it over to someone. They just do do things manually. They just do things in an in a manual way. So I used to have these forms and I would have these test uprunners. I would write these pages or these applications that just have fields with buttons like button one, button two and they would invoke the code that I would want to to run and then I would be the only one who knows how to run these small applications and when they passed I would know that my code is okay and almost all developers have those kind of little test drivers but what we're going to try and do is to use a common framework that will allow us to do things in a repeatable way because the point is that we want to test things in a way that allows

[41:20] (external link, opens in new tab) Everyone in the project to run our test. And we want it to be easy because it was if it was difficult to write tests for our code, then we would only test some of our code, which would explain, by the way, why most developers have very little test because they find that a lot of their code is very hard to test. And that is about all about techniques and learning how to write tests for specific scenarios and this is where the XUnit frameworks (external link, opens in new tab) come in.

[41:50] (external link, opens in new tab) Now they're formerly called XUnit because the X stands for the language name and then the unit. So small talk was called sunit. Then there was JUnit, CPPUnit, Ruby. Actually there was a R unit at the beginning as well. But today it's called Test::Unit. I think it was called R unit. Definitely remember something. I could be wrong. But in Ruby it started most people know it as Test::Unit.

[42:22] (external link, opens in new tab) And what's nice about these units is that these test frameworks is that they're pretty much the same in terms of structure. You have tests and tests exist in suites of tests and each test has a range act and assert that you can do and that's basically it and then you mark them in some way either by name or by attribute or something and then you have this runner that either a command line or a UI or both that just go through the code and look for the naming convention

[42:53] (external link, opens in new tab) And we're going to look at some of those frameworks today. We're going to look at Test::Unit, Minitest and RSpec to understand the difference some of the differences between them and we're going to have to choose one for the duration of the course. We're going to talk about RSpec specifically because it's one of the most used frameworks in Ruby today. Without knowing it, you're pretty much lost for most of the open source code you're going to read.

[43:23] (external link, opens in new tab) And also because having an a thing something built like RSpec does have some advantages over standard XUnit based frameworks. Later in the course we'll talk about other tools for acceptance testing like cucumber steak and turn. You know there are things that you never imagined that you were going to say. It usually happens with my kids,

[43:54] (external link, opens in new tab) But I find that it happens a lot in the Ruby world as well. It's like, I can't work with this turnip. Now, I know the guy who created cucumber, his name is still isoly. Now, if you're not a Nordic person, that name sounds hilarious. And when you find out that he created a framework called cucumber, it's even more hilarious.

[44:26] (external link, opens in new tab) And then you find out steak and turnip and spinach and all these other things and you realize, yeah, there's something nice about this culture that I really like. Especially if you come from a Microsoft world where everything is enterprise framework library one service pack 3, which is the b the best name you're ever going to have. Anyway, I digress. Let's talk about Test::Unit. Now the the basic structure of these frameworks is that they all have

[44:57] (external link, opens in new tab) They all have in xunit they are all methods inside a class and the class is usually located in a name space that's under unit and the and the test method usually starts with the word test. And the word test is important because this is how you tell the testr runner to run your code. And then everything that happens inside this code is being run automatically by a test runner (external link, opens in new tab) in a command line hopefully in a continuous integration (external link, opens in new tab) scenario like we we explained before.

[45:34] (external link, opens in new tab) Now the three things that you can see here are the basic structure of any test that you're ever going to see and they're called the triple A factor. the arrange act assert. The arrange part is where you create the object and you arrange the environment or anything the state of the application to suit your test. The act part is where you actually invoke the entry point and in our case the act is where you invoke the entry point to the unit of work.

[46:07] (external link, opens in new tab) Okay, the public method. And then the assert part is the most important part obviously. You'd be surprised how many tests don't have asserts. The reason that that happens is because a psychological mistake is usually made in project management levels where the team is given a target and the target is code coverage (external link, opens in new tab).

[46:41] (external link, opens in new tab) Instead of code quality. So when you give a team a target of code coverage, what happens is that the team starts optimizing to write tests for code coverage. And that means we don't necessarily need a service to get code coverage. So we just write tests to execute code, but we don't check that it actually works. We get 100% code. Does the code work? Nobody knows. It doesn't throw exceptions. That's what we know. And that's an example of of the fact that most if not all problems in software are people problems not software problems.

[47:18] (external link, opens in new tab) So whenever you see frameworks never think that frameworks are the solution for what you're trying to do. When I say that assert is important I'm really I really mean the fact that that quality is the important thing. You don't have to have the assert unless you really want to have some quality. Okay. So assert equal that's the standard way to say that something is expected. If it fails we're going to have a nice error message

[47:50] (external link, opens in new tab) That shows what what actually happened. Now I can I can actually go and show this