How to Write Clean, Testable Code

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>> Open source projects. Recently, his interest in test-driven development turned into testability explore and JSTestDriver with which he hopes to change the testing culture of the open source community. If everyone could put your hands together and welcome Misko Hevery. >> HEVERY: Howard, you are too kind of words here. So thank you all for coming. We're going to talk about something different today. I want to know what psychologists test around and talking about how to do the testing or more interested in. What is actually going through our heads when we are confronted with writing a test and how we look at this particular thing? So hopefully it's going to be a little bit different. I love questions, so please ask as many as you want and to encourage questions, we have a little bit of schwag that Igor over here will help to hand out. So for good questions, we'll give schwag. So do ask. Don't wait for the Q&A session at the end. Okay, let's get started. So I'd like to--or rather, my boss always like to say "Testing is not like frosting" meaning that you can't bake a cake without sugar and then decide that you're jut going to frost the cake on the--after the fact. Like, it just doesn't taste right, right? You got to put the sugar in the process of baking. And so it works the same way for testing as well. You can't just build your project and then assume that after the fact you're going to, like, sprinkle on some test and then everything is going to nice and fine because it doesn't work. It just doesn't work after the fact. So, let's look at how we actually build a code. And I think whether you do testing or not this is basically how everybody does it, right? The red area is where you put the bugs in and then in the green area is where you take the bugs out, right? I mean, that's kind of how it works. And it's an endless cycle. Like, whether you do test driven or whether you do manual, you code for a bit and then you try it. Like, you can maybe code for half an hour max, but after that, like, you got to try whatever you coded up because sooner or later, like, you're just going to get yourself off. Like, it's an inevitable process whether you like it or not. And in some point, you decide, "Yeah, that's a good thing. I'm going to check it in," right? But the thing is is in the green box and that is that we do it manually or a lot of us do it manually and we'd like to transition to do it automatically. And so the difference is writing your test or rather deciding what you're about to implement and then writing a little scenario, a little story about it and then implementing it and we'll talk about that later. So the whole trick is how do we transition from us doing the test to a machine doing the test for us? And so the way most people kind of look at it is they kind of do this. So they say, "Well, we'll put the bugs in over here and then we expect that they're going to have some kind of QA folks that--it's not our problem, right? It's like software engineers. We write the code and then somebody else's problem is to kind of make finer bugs after us." And then at some point people will say, "Well, the whole QA manual, QA doesn't really work out so we got to have some kind automated test." And so at this level, people are always looking for some kind of magic, like "If only we bought some software X, then all of our testing problems will go away." And there's lots and lots of companies that do this and it never works. And the reason is it's too late of that particular point in time. There's another problem with this picture and that is that the people who put the bugs in are not the people who feel the pain. And unless you have the close circle, unless you feel your own misery, you'll never going to learn and get better at this particular thing. So the magic to testing is not here. It's up here. It has to happen up there. If you do this right, everything else will just fall automatically in place. And so we'll talk about this a little more. So, the funny thing is you come to an engineer and you say, "Why don't you write a test?" And they always have some kind of a beautiful excuse for you. And usually it goes, you know, a long list. I'm not going to go into individual details as to what all these excuses are, but one thing that nobody ever says is "I don't know how." That's a valid excuse. You know, just say "I don't know how." Like it's perfectly valid, right? I mean, if I come to you say, "Do you know JavaScript?" You can say no and, you know, it's not shameful to say that I don't know a particular language. But somehow it is shameful to say, "I don't know how to write a test." Now, why is that? It's a skill like any other thing, right? You could say, "Do you know how to ski?" And it's not shameful to say, "No, I don't ski." But somehow as engineers it's kind of programmed into our head, "Of course, I know how to write a test." Nobody ever says they don't know how. And, really, this is the reason why most tests don't get written is that writing tests is a skill like any other skill and you have to learn it like you learn all other skills. It's not innate to the whole process. So, let me demonstrate this thing. This is my favorite interview question when I do interviews at Google. I say, "Suppose you're an evil engineer and you want to make hard to write code--I'm sorry, hard to test code. What do you do?" It's an open question to all of you guys. What do you do? Yes. >> Put singletons on the code. >> HEVERY: Okay, so you've been reading my blog, good. Yes. >> Yes. I'm sorry, but he... >> HEVERY: He said put singletons everywhere, global states in other words. What about--so I'll give the gentleman some schwag to motivate. Yes? >> I think you could, you know, put new operators. >> HEVERY: Yes. Put new operators everywhere. Okay. >> Do the hard code dependency. >> HEVERY: Hard code dependency, it's a different way of saying put news everywhere, yes. >> Lots of static method. >> HEVERY: Lots of static methods. This cannot be overwritten. >> Overload a lot of things in hard ways. >> HEVERY: Yes, that's general just complexity, right? >> Yes. You increase complexity and you'll blow the [INDISTINCT]. >> HEVERY: Right. So that's an interesting statement. So a lot of people say that on an interview that, "Oh, I'll just make it really complicated." And while it is true that that makes it hard to test, it doesn't--that in itself doesn't fundamentally prevent testing. It just makes it miserably difficult, right? Whereas... >> [INDISTINCT] >> HEVERY: Yes. >> [INDISTINCT] >> HEVERY: It makes it miserably difficult. >> And it becomes very difficult to understand. >> HEVERY: Yes. Yes. Whereas, new operators fundamentally make it impossible, right, that's the kind of the difference that I'm going here. But excellent. I mean, you guys know a lot of good points. I get a lot of blank stares. People are not quite sure what they're--what do I mean by this particular question. But I figured if you know how to write hard to test code then you probably know how to avoid it, right? So let's talk about less theory and a little more hands on. So I'm going to have some examples to go through. And, yes, this is kind of hard to see, but I was kind of hoping that some of you would have computers and you can just go this URL and actually see the code. But it doesn't matter. We'll go through them in more in-depth in particular thing. So, usually in an interview, I present the candidate with a piece of code something similar to this and I say, "Pretend you have to write a test. And specifically, I'm interested in what would you change about this code in order to make this testable?" And, I mean, you're squinting. There's going to slides that I zoomed in a little more, so don't worry about it just yet. The interesting thing about this question is that everybody immediately takes a piece of paper and, you know, sticks their nose into it and tries to understand what the code does. And this is interesting because why would you think that knowing how to write a test is related to knowing what the code do--does? In other words, whether or not a piece of code is easy to test, it's a function of structure of that code; it's not a function of what the code does. And this is something that people don't seem to understand. You were going to say something? >> Yes. The problem is a lot of code bases don't have mission statements or specs that are simple to understand and it tends to make the code [INDISTINCT] spec. >> HEVERY: Yes. >> And that's one of the reasons why you get that let-me-see code... >> HEVERY: Right. >> ...because the spec are inherently non-existent. >> HEVERY: Right. So the message I'm trying to say here is that when you're presented with a piece of code and you're trying to say, "How can I test this?" don't try to understand what the code does. That's really a secondary problem. Rather look for how the code is structured. And it's something that you can train your eye to do over time and I'm going to show you what I look for when I see a piece of code and how I really don't think that what the code actually does is relevant to this question. So this particular code is designed to slap you around. It's the most evil piece of code I could think of and from--I was really inspired over the years of looking at our people's and also mine, especially if I go back in history. So then a couple of red flags that I look for and I think, you know, we should learn to recognize as a software engineering industry is global state and singletons, somebody already pointed that out; Law of Demeter Violation and I'll have examples of why that is a problem; global references to time because, really, time is a global function that changes underneath you all the time; hard-coded new operators and just the general lack of dependency injection or really dependency management. So let's do an exercise. Let us assume the code is fixed, right? This is where most people, when they first come to testing, they have this assumption, "Oh the code is already written. I've done all my work and now I'm going to go on and try to write a test," right? And so, I think this attitude that the code is perfect I'm just trying to write some tests on top of it, all right? And that's--what I'm trying to say is that it really is just the wrong attitude to look at when it comes to writing code. So--but for this exercise, let's just assume for a second that the code is fixed and we're going to try to do our best effort in running a test for it and the process that we're going to identify problems with it and then maybe hopefully in the end we'll suggest some refactoring. So, hopefully, this is a little more zoomed in. The part on the left is the code that I'm looking for and then that part on the right is the test case. I'm going to try to write a test. Keep in mind that this is what would the world look like if we assumed that the code is fixed and we're just trying to write a test and this code is specifically badly written to make testing really, really difficult. So the first thing I look for is the static key word because that tells me that there's global state. Now what does it mean? What it means in test is if there is a static keyword here, it means I only get a single chance of instantiating this thing in my test system, right, because it's a--once the inbox synchronism is instantiated, I can't make a new one because it's final, right? So, good old singleton, which means my test set up will have to have some kind of like--if you're not initialized, initialize it. And the trouble with that is you really don't know when the class gets initialized. You're hoping that you are the first class to reference this thing, but you have no idea. So, this is a big assumption whether we can even do this in our test. But nevertheless, we'll kind of break down to this thing. Let's see what else--the piece over there. Referencing to more global state over here. The next thing I look, it is the constructor and I say, "Well, it looks like I need a certificate but I need to read it from it looks like some config object that I have to pass some path property that I have to indirectly look up and then read the certificate from the file system." So, the equivalent on the other side from the test point of view base is, well, you got to generate a certificate, you got to attempt to create a temporary file, you got to write the file somewhere, close the output stream and then you're hoping that the configuration object is writable. Now, given that this--whoever wrote this piece of code was paranoid, chances are it was also paranoid here, which means this assumption I'm making that I can actually modify the configuration object might be outside of the scope or was actually possible in that code base. So that might be troublesome as well. So, here in the next piece is, you know, I'm getting a user, username and password. And in order to do this, I'm going to have to, let's see, create a user object. And here's another big question mark is can you actually instantiate a user without any arguments? Like it could be that the user requires a whole bunch of other things, for example, the person could have decided the user's constructor takes a servlet in order to get a hold of the cookie, in order to initialize itself in the cookie, right? If that was the case, then instantiating the user would get really, really difficult. So here I'm making a big assumption that the users are going to be actually easy to instantiate. I'm going to put the username and the password in there and I'm going to be able to set it on a config. Again, these are all assumptions that one would have to make in order to write this test. And these are really tough assumptions to swallow. What else is in there? So then when you do all this, the next piece of code I look for is these are your true dependencies almost--usually. Not always, but usually the true dependencies are the fields that you have stored in your object and therefore, those are the things that you should--that you would want to mark and inject, but that's not actually what you're asking for inside of the constructor. In other words, you're pretending that what you need in order to get your work done is the configuration object. But the reality is you need all these other stuff and you are actually going to go look for stuff, right? So, I always like to say "Ask them what you need. Don't look for things." This is what I mean by that. Like, you should be really asking for the certificate username and password directly rather than looking for all these things. So, look at all the code that we have written over here. That's actually quite a lot of code to just get through the constructor and the constructor is something that we need to do all the time when we're testing. So, let's go further. So now here's the sync method we want to test. And, again, there's new operators here but this is a tough one. It's going to say "Make something called pop connector," doesn't matter what it is and it's just going to connect to and disconnect, which means you'll probably have some socket communication going on. So in our set up method, we'll probably going to have to create a server because there is no way to give you a fake connector over here because there's a new operator here, right? So, I can't instantiate something else for you over there. So in order to test this, I actually have to have kind of a fake server, which is going to have real, you know, TCP/IP stack to get it up and running. And then, hopefully, [INDISTINCT] I will be able to shut it down, which is also usually a big question with servers whether you can actually shut them down. So, they--these are just assumption after assumption after assumption I'm making that if everything else is written in a correct way, then maybe I can test this untestable piece of code if I just jump through all these hoops, right? But the reality is if this piece of code is not written well, chances are the rest of the system isn't written well either which means many of these assumptions I have made over here are probably false and will probably require a lot more of code to get this thing tested. Right. So, paying attention? Did we forget anything? No. Well, we haven't written the test yet. This is just the set up, right? We're just trying to get through the new operators so that we actually get an instance of this thing. So when you survive that crazy set up we just one through, then maybe you can write a test that actually does something. And what's interesting when people write test after the fact, they'll usually say, "testSync." It's usually a dead giveaway. What exactly does that mean? Does this tell you a story of what you're trying to do? It doesn't really tell a story here, right? You're just--you were told by somebody "Go that--test that class." You saw that that class had a single in method called Sync so you made a test called testSync. And then, you hack that in until you were able to get something to execute and then you said, "Well, I'm executing stuff and it looks like I've got some coverage, so ship it." So, here's another mental exercise. Now that you've written this test, which is a little scary, could you reconstruct your project if I deleted your source code? That is, I--you get to keep your test, but the production code is gone. Would you be able to be--would you be able to reconstruct your code base? I'm going to say you're going to have a really, really hard time. And the reason for that is tests like these, they don't tell a story. They say if you do all this set of crazy things, then the following set of crazing things should be true but, like, they don't seem to be related. Why is it that I have to be writing certificates to this special file and setting these global variables? And, like, it's not--there's no rhyme or reason to this thing. So, let's see if we can flip it around. So before we can flip it around, I'm going to say tests are so yesterday. It's all about the executable specs and executable specs are just tests with syntactic sugar and sugar is always good and makes things taste better. So how many of you guys have heard about specs? All right, good. Do you like them? >> [INDISTINCT] >> HEVERY: Really? What language are you familiar with the specs in? >> [INDISTINCT] >> HEVERY: I don't mean that kind of specs. I mean like specs as an executable test in our specs. Well, I'll show you an example in a second. Who else raised their hands? And do you like specs over tests? >> [INDISTINCT] >> HEVERY: Yes. >> Yes. I use [INDISTINCT]. >> HEVERY: You are? Okay. So we're talking the same language. >> [INDISTINCT] >> HEVERY: Yes. >> Yes. I use BDB, but it's also [INDISTINCT]. >> HEVERY: Yes, BDB is another fancy word for this stuff. It's just that--I mean, people laugh. The BDB is really just testing done right. So, let's change our assumptions here and that is assume the code we were about to test is yet to be written, right? And the other thing--assumption you want to do is that you want to demonstrate a single behavior per spec or rather, to put it differently, we want each test to tell a story, right? Imagine I tried to explain to you what a particular product does. What I'm going to do is I'm going to tell you a lot of, you know, a lot of examples, like, I you would say "Oh, if you do this, this and that, then this is going to happen. And then you if you do this other set of things, then this other thing is going to happen." And then once you--what humans are really good for is that if I give you a series of examples of how something works, humans are really good about generalizing. So if I say, you know, it does the following set of things and the following set of things and the following and then you say, "It's a car," right? But the other way around, it doesn't work so well. If I come to you and I say, you know, "It's a sinker for email" and you're like "It's too general of abstract thing. I don't really know what it means concretely." So what it means to tell a story is that I give you a whole bunch of examples that demonstrate how that piece of code work. Let me show you that. So, the biggest difference--and this is Java and the Java's BDB is a little--not as good as, for example, Ruby's BDB or in JavaScript there is Jasmine that has pretty good BDD syntax. But the basic idea is that you want to tell a story. And so, you're saying itShouldSyncMessagesFromPopSinceLastSync. "Oh, that's a story. Like, I understand what it means. It's much better than saying testSync." It should close the connection even when an exception happens. I understand what that means and I understand how to code that, right. itShouldSyncMessageOnlyWhenNotAlreadyInTheInbox, right? So, these are the specifications of what that particular code should do and then we fill in those specifications with little stories to demonstrate this particular scenario. So, let me give you an example. So, each of the sync message from pop really doing, you know, something like that. I'm going to create a pop class, I'm going to add two messages to it then I'm going to connect my syncro, whatever that means. I'm going to pass in all the dependencies like pop, inbox, filter, when was the last time I synced, and then I'm going to say, "Sync now". And then I'm going to assert that this particular message actually got copied but there's two messages I put in but only one got copied because one was already synced because it was after a particular date, right? The test like this tells a story. And the other thing that test does is notice that it's much easier to follow. There isn't all these destruction about writing, creating certificates and writing them to temporary locations and then cleaning them up and starting servers and, you know, all--all just destructions that aren't telling a story. So, you want to make sure that these are short, you know, they fit on the page and they're easy to read. And to me, that's really that's really what specs are, little stories like that. So, maybe another story would be, you know, tested--you should close a connection even when exception happens. So here again I can create a filter that throws an exception and I can simulate an exception throwing--because when I try to sync, I expect to catch it. Again, I'm telling a story. And something like this wouldn't be possible if we already have written our implementation and had new operators everywhere, which most people have. So notice is another thing, that if you do this right, there usually isn't a set up method, right? There is really no--the set up method is needed because you need to execute some complicated piece of code, right, which maybe has some if statements, loops or something like that. All we're doing here is we're instantiating an object and wiring them together and as a result, your set up is essentially just declaring your fields. And these fields actually become part of your language. They become kind of like a DSL, right? So, now, your test reads nice because you can say, let's see, right--you have these things like long ago, past, now, future, inbox, filter, long ago message, past message, current message. And then if you're trying to write a test, can we go back up a second, like over here, notice that you could say add message long ago, add message, past message. And then when I do a sync, I say sync now or last time I synced was long ago so that it reads more like--I'm trying to--the DSL--do you know what DSL stands? Dynamic--Domain Specific Language [INDISTINCT] I'm trying to get out. So, you want to make sure that you're--you really tell a story with these things. Now, I can do all of these is because none of the code was actually written yet. I'm just making it up and it's easy to make stuff up. And the other things is if I'm making it--this stuff up, nobody, really, in their right mind will start making crazy things up like, "Oh, let me start a server. Let me write a bunch of things to a file system. Let me create some obscure or global variable that I set," right? You don't think that way. You create the most simple scenario that you can think of because creating complicated scenarios is a lot of effort, right, and nobody really wants to do that. So, writing the test before the code is written, it really allows you to just think about it in a really pure form and then worry about how these pieces get together or wired up later. So, given the specs, let's write an implementation. And so here's an example of what an implementation would look like. And the first thing, what do you notice? What's the difference between the previous one? >> Shorter? >> HEVERY: Maybe. It's about the same, I think. But more importantly, with the exemption of the new date, there is really no new operators here. They just got an exemption because it's kind of a value object. It's like [INDISTINCT] strings, so it doesn't really count. And I'm really not creating a date from global timer, I'm really passing in the date because it's being passed in. So it's kind of an exemption here. But in general, you don't have new operators. So, for example, the most important one is that the pop connector, which was actually the thing that connects to the server, is now passed in from the outside so that I can pass in a fake implementation if I choose to. So, when you look at a code like this, the thing that I would look for is I either want to see a whole bunch of new operators and know if statements or loops or a whole bunch of if statements but no new operators. Because I either want to see a code that's a factory, in which case I'm wiring things together and I'm not doing any work or I'm doing work in which case somebody else is responsible for wiring process because if I mix the two together, then I'm going to have a really hard time writing a test. The other thing you're going to notice is that a good constructor looks like this, and that is list your dependencies. Unfortunately, in Java, we have to do this three times. We have to list your dependencies once here, then the once over here and then once copying over. I think Scala has fixed this. But this is what a good constructor looks like. You know, you just say, "I need a whole bunch of these things. It's somebody else's problem on how you get there." So, what have we learned? We learned that the new operators are really the thing that gets you. If you need it, then you must interact with it. So what does that mean? I don't have a slide. The slide was too far back ago. But in the original code, there was--we were asking for, for example, for config and we really weren't interacting with the config. All we were doing is we were getting stuff out of it, right? And so, the rule is to not be violating the law of the Demeter or you must be sending a message for the other object to do something rather than sending it a getter, right? That doesn't count. So, passing it in a config and then having the other objects say config.getuser.getusername just so I can take the username and password to the pop connectors so the pop connector can connect on my behalf is kind of very convoluted. Rather, you should just say, "Look, I just need a pop connector and it's not really my problem how you construct one. It's somebody else's business and that whoever constructs it should be passing in the username and password and certificate as well." >> What about passwords? Are they valid [INDISTINCT] the user? >> HEVERY: The factor is valid in all of these. You can be basically more lenient with them, right? Because they're... >> Can you repeat the question please? >> HEVERY: Oh, sorry. The question was do factories validate Law of Demeter because you don't really interact with them. And to some degree--I mean, you--the factories shouldn't because you're just instantiating a whole bunch of things and passing it back in, but sometimes you really have to say, "Well, get that object from over there and pass it over here." So, to some degree, you can be lenient about Law of Demeter violations in factory code, but not too lenient. Even there, it just looks kind of suspicious. There was a second question over there? No. Okay. Oh, yes. And lastly, doing work in constructor is just a bad idea because constructor is one method that I cannot over ride. It's the method that I absolutely have to call every time I want to call--instantiate--any call--any method I'm [INDISTINCT]. And so as you put work in the constructor, you're basically saying that every time you want to do something with this object, you better be prepared to go through whatever that constructor wants to do. And if that constructor wants to read certificates or wants to, you know, send e-mails, well then you're stuck. You're sending e-mails and you're reading certificates. So--and the last piece is that what we talked about is we talked about unit testing, which is an individual levels all way at the bottom, but the same rules also apply for end to end tests. To give you an idea, a unit test is to end to end test as classes are to components. So, if my class has followed proper dependency injection so that I can instantiate some individual classes, then I should also be able to do the same thing for components. So if I have an application--I don't know, I'm going to use Gmail as an example--but if I have an application like Gmail, I should be able to say--in the end to end test, I am going to remove the authentication component and then replace it with a fake always authenticated component, so when my end to end test run, I don't have to worry about logging in. And that's another form of dependence injection, but really just slightly higher level. So, just like your individual classes need to be properly managed and you can replace them through dependence injection, the same rule really has to apply to components. And it turns out that if you do class injection properly then you get the component injection kind of for free. So, if you do your unit testing properly, then your end to end test should just kind of fall into place. So, what are components? Well, just well-defined [INDISTINCT] like I just pointed out, like being able to replace the authenticator with something else or maybe the persistent engine with something else or something like that. Your app should really be global state free because global state is going to hunt you whether it's with unit test or end to end test or at any level. You really don't want to have any. And last piece is that when you are testing a unit test--you know, we kind of went to the idea of creating a DSL out of your variables so that you can easily write sentences. That is even more important with end to end test because when--in the end to end test, usually you want to tell a very complicated story, while in the unit test, you might say something very simple like "If I have an account and I deposit five bucks to it, then the account should have now--you know, if the account started with $5, I deposit $5, I should I have $10 in there now." That's an easy story to tell. But if you have an end to end test, the stories you're going to tell are much more complicated. You probably are going to say a story along the lines of, "Oh, the user logs in as--you know, into his Gmail account and then he checks the inbox. And then if he sees an unread message, he clicks on it and then that unread message then when he goes back to the inbox, it should not be showing as unread but it should be showing as read and then he should be able to unread that." It's a very complicated story, and unless you have a very good DSL, a good Domain Specific Language of telling the stories, you're going to get lost in the process of building your end to end test. Last thing is I oftentimes demo how I do test-driven development, and so I usually show up at a meeting and, you know, I have Eclipse going or whatever your favorite ID is and I start typing, I had saved and all of a sudden my test run. And what that really means is you really have to spend time on making the environment easy for you so that it doesn't fight you, right? And I oftentimes forget that I have maybe spent, you know, four hours trying to get a Eclipse to behave properly so that when I do a demo to the students or whoever I'm giving the demo to, they kind of see, "Oh, look, if you hit save it just magically runs," right? But that magical part is actually the hard part. When we're talking about being able to write test, it's not just being able to write test, it's also being able to set up your whole environment that the environment doesn't fight you every step of the way in writing those tests. And that's the kind of the tricky part to the whole business. So that when you set everything up, of course, it's easy, right? If you come to a project where everybody does this thing and you're a new kid on the block and they just say, "Look how easy it is to write a test." And then you say, "Well, it take me three lines of code to write a test or I can start a server which takes 30 seconds. And then I can go and log in, which takes another 30 seconds, and then I go to inbox and I click on the message and I can verify that, you know, the message is no longer unread but it's read or I can just run three lines of code." Of course the person is going to write three lines of code because it's easier, right? But getting to that point is something that is a lot of effort and it's a lot of knowhow. So a lot of times when you're talking about having--doing test-driven development, it's this unspoken thing that nobody talks about which is that how do you get your environment set up so that these things are natural, so that it's the easiest thing for you to do? And that's not exactly simple or easy and sometimes finding the right tool is tricky. So, I want to talk about one last thing which I call the Theory of Bugs. Yes. >> I have a question. >> HEVERY: Yes. >> When I started testing as an intern at IBM, we were testing in two different environments [INDISTINCT] which is the backend of WebSphere >> HEVERY: Yes. >> And the problem was I was given like a junk file that was in Python and XML, but it was really hard to test from one environment that they'd been using into the new one. >> HEVERY: Yes. >> So how do you that when you're testing in multiple environments because that had to do with software as well as the web where... >> HEVERY: Right. >> ...you need to test in different... >> HEVERY: So you're asking an open-ended question of... >> Yes. >> HEVERY: ...you know, "We were in a situation where it's hard to test. What do you do?" Unfortunately, there isn't one magical answer I can give you. It's basically fighting the environment trying to figure out some kind of a harness or something to make your life easy, and there aren't really easy answers to it. There just has to be this goal and this vision that you have to have that is if I somehow solve all these problems, and for every project the set of problems are different, then--and I get to this happy place--then it--life makes sense and it's easy. But getting there is always the tricky part and unfortunately I don't know or it can't really help you on an individual basis. Did you--thank you. >> You're basically circling on the same concept that all testing [INDISTINCT]. >> HEVERY: Yes. >> If you're testing hardware, even mechanical hardware, you have to build it with testing out [INDISTINCT]. >> HEVERY: Yes. >> You have to build--if you want interchangeable parts, take it back another 150 or 200 years. If you want interchangeable parts, you have need to change rules. >> HEVERY: Yes. >> You can't do it by hand. >> HEVERY: Yes. >> Because if you do it by hand, you won't have the interchangeable parts. If you don't have interchangeable parts, you can't attach the test [INDISTINCT]. >> HEVERY: Yes. So the thing you're bringing up is that the cost of making a mistake in hardware world and in anything but software is just so expensive that people have no choice but to test. Unfortunately, in the software world, the cost of making a mistake is that feeling you get like, "Oh, if I just fix this, everything's going to work" and then you just iterate on it for three months, right? >> Yes. >> I know. >> But the only problem is, as you noted, the trivial case, it's easy to do by hand. >> HEVERY: Yes. >> The large case is difficult to do by hand. >> HEVERY: Yes. >> However, along that path, that one meandering path from A to B, it's not clear that there's a bright light at any point that says, "Here's the cliff." >> HEVERY: No, there's isn't a cliff. Yes. >> [INDISTINCT] >> HEVERY: Yes, this... >> [INDISTINCT] no problem and all of a sudden, wham. >> HEVERY: Yes, it's--yes, it's--there's no cliff. You're right. That's a good way of putting it. >> And yet the cliff is clear in retrospect. >> HEVERY: Yes. >> [INDISTINCT] >> HEVERY: Which is always why it's so hard to sell somebody any idea of testing because there's this mountain in front of you and I'm saying, "Look, if you just climb that mountain, on the other side, there's a paradise." >> Would you suggest that we go about software testing as if it was hardware testing, as if--or designing things in a way that... >> HEVERY: Yes. >> ...the [INDISTINCT] would be really bad as opposed to just something? >> HEVERY: Absolutely. Testing is something that has to be baked in. From the moment you start writing the first line of code, y have to think about, "What am I going to do to test this thing?" And really, better yet, you need to have the harness ready before you write the first line of code because then you don't have to say, "Well, am I sure that this design is actually testable?" Well, you know it because you wrote the test first and then you write an implementation. >> So, the test and the design are interconnected? >> HEVERY: They are very much interconnected. And it's something that people fail to acknowledge, especially when they have this attitude that you can just sprinkle the test after the fact to the code base and you just can't do that. >> Even the--if I may. The test--the concept between test gates on [INDISTINCT]... >> HEVERY: Yes. >> ...is easy to do upfront than part of your [INDISTINCT] and it's viciously productive. >> HEVERY: Yes. So let me finish a couple of slides and then we can throw this into an open-ended conservation. I promise I only have about five or so more slides. So, I call this a theory of testing and that is, I think there are three different kinds of bugs. So, one bug--kind of bug is what I call the scorpion, which is you thought about the problem and you thought wrong, and so it's a thinking a mistake, right? The other kind of problem is you got the pieces right, but you miswired it. Like, you got yourself a stereo system and you got yourself a TV but you plug the input to the output. I wouldn't call it really as a thinking mistake, it's just you happen to miswire the pieces together and that happens sometimes. That's a--that's a--it's a wiring problem. And finally, the lady bug problem, which is you put it together then it just looks funny. And this is your UI, right? Like you put it together it just--it doesn't quite look right. And why do we--why do we talk about these two different tests? It's because each one of these kinds of bugs can be caught by different kinds of tests. The thinking problem is really your if statements and your loops, and that's where unit testing is really wonderful at, which I have a class and there's a whole bunch of complicated if statements and loops and logic and I want to make sure that I test those particular things. And so, unit testing over there is what--is what the doctor has ordered. Once you know that individual pieces work together, you could have very well miswired the factors, right? Unit tests don't help with that. What you need isn't kind of an end-to-end or at least medium level test to make sure that pieces work together. And so, this is where end-to-end comes in. And that basically verifies that the--at least the happy paths work because if I can get the system up--like, I like to use the example of a car. You know, if you put the car together and in the end line if they put the key in and they start and they drive out of the parking lot, chances are they got the right parts in right places, right? I mean, for the most part, you know, the battery is there, the fuel's there, it's hooked up properly, the engine's there, the brake's there. So, for the most part it looks like nothing got forgotten. You don't know whether the car will perform at some really cold temperatures or will it be able to perform, you know, with the ABS and all the other stuff but, hopefully, there are other unit test that kind of proved that yes, the brake system works when it's minus 10 degrees or something or like that. And so, really, we're just interested if the pieces got put together properly. And so, that's what unit tests are for. They're really not meant to make sure that every single scenario works. They are really meant to be that the basic flow works because that proves to us that we wired this thing together properly. The last one, I don't have an answer for you because that's like the font color is off or, you know, it looks weird when it's on some weird [INDISTINCT] monitor or maybe when you translate it to a different language, it doesn't look right. And so, unfortunately, there is no test for this. This is still humans, so we can't really automate this thing. But there's a good news and that is the probability of finding a particular bug of a--of a particular category and the cost of fixing this particular bug is different. So, logical bugs, they're really, really--the probability of you finding a logical bug is really high. You make lots of them everyday, you fix lots of them everyday. You probably don't want to think about it because you--as you're changing the code around. The difficulty of finding it is actually hard. Like, if something doesn't work and only understand some cornered cases, it's really difficult to find these things. And then when you find it and fix it, the probability that you introduced a new bug is actually quite high. So, luckily for these set of bugs, the logical set of bugs, you know, we have unit test and that's what verifies us that everything is kind of working and we're not regressing. The wiring bugs, this is the end to end test, right, chances are, usually, they're kind of easier to find because if you miswired, usually the whole system just crashes spectacularly, you know. Like, it's not like a logical bug where only on leap year and at the end of the year, at 12 o'clock, our system doesn't work, right? The wiring problem, usually if you have one, the system just doesn't come up. It throws an exception on the main method or something crazy like that. And so, fixing those is much easier, finding them is much easier. And when you fix it, chances that--of you introducing a new bug is actually quite low. The rendering bugs are the easiest to kind of spot, to fix, but there is no real way of making sure that we don't reintroduce them, right? That's kind of the end of it. But when I explained that, people say "But my code is different. You know, I have a super bug." That's because you mixed all the concerns, right? The reason I could have two different kinds of tests is because my code was either responsible for wiring or it was responsible for logic. I didn't mix it together--or I had, you know, the rendering the UIs or whatever the view templating system you have--happen to. I didn't mix the pieces together. When you mix the pieces together, all bets are off, right? You have the super bug where it's a rendering and a wiring problem and a logical problem all tied together. Okay. That's everything. So, I'm open to questions if you have any. Yes? >> Okay. So I have two questions. >> HEVERY: Yes. >> One comes from a work problem I'm having right now, which is what is the best way to test multi-threaded codes? >> HEVERY: That's always the question that always comes up. So, the best way that I can come up with is that you separate it into two problems. So, one is there is the problem of being the traffic cop, making sure that your--your threads don't deadlock and making sure the right stuff is happening at the right moment. That's a deadlock problem or rather a traffic problem. And then the second problem is the actual work that you have to do. So, you need to structure your code in such a way that this--you can schedule the threads in any point in time so that you can simulate what happens if this thing runs first and that thing runs first. And that really means that you have to separate the scheduling from the actual logic that is happening inside of the scheduled blocks. >> I mean--well, the way that I'm going to approach it was that once you set certain frame points... >> HEVERY: Yes. >> ...they would meet at each point. >> HEVERY: Yes. >> They'd be able to [INDISTINCT]. >> HEVERY: Something like that. There is really no good easy answer. >> But then, I realized even though, for me, it was like a one-line code, it's several lines of codes. And even if it's... >> HEVERY: Yes. >> [INDISTINCT], there's no guarantee. >> HEVERY: And it's--what you're doing is you're having flakiness, right? Because even if you test, sometimes it work, sometimes it doesn't. It doesn't prove anything. So, threads are hard and there is no--yes, a hard question. But so digress a little bit, I think the answer to your problem might be something like [INDISTINCT]. How many people aren't familiar with [INDISTINCT]? And interesting there is you have a single threaded system, therefore these problems don't exit, and instead of scheduling, you have callbacks that execute this [INDISTINCT] system and you're guaranteed that each callback happens by itself, exclusively, so you don't really have any thread-locking issue. And so, there you can--you don't really have this particular problem. But that's digressing. So, the answer to the question is look at the [INDISTINCT], maybe that's what you're looking for. >> And then another one is there's a difference between whether some are logically correct where they're not optimized [INDISTINCT]. >> HEVERY: So, you can have a little micro benchmark test in your code. Those are trickier. So, I usually have a separate test suite that runs them so that they don't pollute [INDISTINCT]. But there' no reason why you can't say, you know, "I should be able to run 10,000 operations in a second" and then just run it for 10,000 operations, measure the time. Yes, it might be flaky because you set the threshold to early write, but the other thing that you might want to do is you might want to collect these numbers. So, one thing is to fail a test outright, but the other thing which you can do is you collect these numbers and then you put them on your continuous build and you make a chart. If you use Hudson, it's actually--there's a plug-in for Hudson that allows you to collect these things and enjoy them. So, as long as your test produce some kind of a properties file that has all these numbers of performance, then you can chart them and then you can see as--commence--go in to see if any of them as regression test. Yes, it's kind of a visual. So there's really no 100% guaranteed way. >> Regarding the running testing [INDISTINCT]. So, you put a lot of codes [INDISTINCT] and you not only [INDISTINCT]. It's not perfect, but it's [INDISTINCT]. >> HEVERY: I think you were first. >> Do you prefer mocks or objects? >> HEVERY: Do I prefer mocks or objects? That's always a religious debate. I guess you can say that I'm the kind of the guy who likes sate-based testing to some degree. I think mocks are good and you should use them. I have almost these rules that if you're going to have to use a mock, you should never have more than two mocks involved in a--in a test. And then if you're going to have expectations on these mocks, then you shouldn't have more, like, three expectations per mock. If you're doing anything more complicated than that, you're doing it wrong. And if a mock returns a mock, if you train a mock to return another mock, you're doing it wrong again. But in general, I find that I don't really use mocks that often. I much rather prefer to do state-based testing, which is that I put the state in a known situation like, "Oh, the account has $10." And then, I perform a story on it which is, you know, add someone into it, subtract and add interest or whatever. And then I ascertain that now the account has a particular amount of money on it. I don't know whether it's like a personal preference or it's actually better or not, so. I mean, I heard arguments both ways, but that's just what I do. >> The test vector. >> HEVERY: I'm sorry? >> HEVERY: Test vector, okay. >> The same thing as you do with a chip. >> HEVERY: Yes. I think you had a question there, right? >> [INDISTINCT] >> HEVERY: Really? What team are you on? >> [INDISTINCT]. But it looks like the performance is kind of like [INDISTINCT] actually you want to do is [INDISTINCT]. So now it's like you do [INDISTINCT] and say, "This is what I always did, so this is the way we should work." >> HEVERY: Yes, it is in a way like that. So, business analysis and all these things are important and it's really outside of the scope of our discussion. The discussion really starts with [INDISTINCT] PM or whoever knows--whoever knows what needs to be built comes to you and says, "We need to build an e-mail program, right, and it should have the following set of features." Then you can turn those features into specs or stories and then the stories is what you end up writing. And it turns out that as you're writing the spec, you're indirectly designing the code because you're saying, "Well, this class needs to do X and in order to do X it needs to collaborate with this other concept over here. And so, I'm instantiating this concept and wiring it together." And in the process of it, you're really doing a design because you're looking at it like, what does the API look like? You're starting from the user point of view. Like, what does the API look like and then I'm going to back track and do the implementation, which is very different than doing the implementation and then really having accidental or incidental APIs is what most of code has. >> [INDISTINCT] >> HEVERY: It's all mixed in together. Like, I don't really see boundaries for it. It's like you write a little code, you write a little test, you run some test, you--something breaks, you fix it. It's just a continuous reiterative process. >> [INDISTINCT] >> HEVERY: I have no idea. I'm really the wrong person to ask about that. But I'm assuming you're referring to Android. I believe that there is a company called Pivotal and they have a testing framework for Android called something robot, electric robot, electric sheep. Something. I can't remember what it is, but they did some--a whole bunch of interesting work around the--around testing Android stuff. I can't remember the name though. It's something about electric and something about a robot. >> Robo-electric. >> HEVERY: Robo-electric? There you go. Robo-electric, it's called. >> Robo-electric? >> HEVERY: Yes. Yes? >> [INDISTINCT] >> HEVERY: What advice do I have somebody--for somebody who was interning quality assurance testing? I don't have a specific advice, but my general advice is that a good way to learn all these things is to be part of some open source project. And the reason for that is because you can try all kinds of different things. You can kind of experiment. And, you know, the worst thing that's going to happen is they're not going to accept your patch and you'd only have some time pressure or anything like that which happens in most companies. So where I learned most of my stuff and I--where I see the people that I talk to where they learn more of their stuff is really at open source projects when they tried different things and tried to add testability to it. So, my general advice is be part of open source and try to do something. And in process of doing, so you'll probably discover and learn from other people. >> So what you're saying is rely on your team that's there with you because there's certain things that you're not going to know and be open to learn and be flexible because sometimes it requires you think of--and then we learn Jython performing live, which is Java and Python. >> HEVERY: Yes. >> So being open to learn those new things to be able to fix whatever defects that... >> HEVERY: You know, I want to make a more general statement about what you just said, it is that there's something interesting about engineers in general and that is that they're really--I don't know if they're afraid or there's something about saying "I don't know" that they're having troubles with, right? And there's nothing wrong with saying that because there's so much information out there. Like, you don't know most of it and you just have to accept it. So, a lot of times when I actually--especially with interns and younger people who are new to work I always find is that they are really having a hard time asking for it and saying, "Look, I just don't know. I need some help." And that's really the best way to learn, just swallow your pride and say, "Yes, I need it. Help me out" and you will learn all kinds of stuff from there. >> [INDISTINCT] >> HEVERY: That's a tough one because you're not--really, it's not a--what you're asking is not an engineering question. So the question was how do you inject process into an organization and that's not an engineering problem, that's a people problem. >> Maybe just on the technical side. >> HEVERY: On the technical side, it's easy. Getting some time with the [INDISTINCT] injection system. If you're working in Java, I like Juice but there's other ones, Spring and I can't think of any right now. >> [INDISTINCT] >> HEVERY: [INDISTINCT] thank you. Both are very good. They're all about the same in terms of capabilities, so they can't go wrong with any of them. And just start at one location of the code base and slowly grow it and then people will slowly see, "Oh, yes, that piece of code base looks much nicer. It's more testable." And just slowly as you need more and more things just grow it from there. From the people's side--of point of view, for a while what I was doing at Google was actually--there was a group called Test Mercenaries where we went from group to group and we try to inject these practices, as you say, into the team. It's very, very difficult. It's very project-specific because each team has different kinds of hang ups or different things, different problems that they're faced with. But in general, what works is that pairing with people works tremendously. Just--you know, instead of arguing about some arbitrary not real thing, sit behind a keyboard, somebody have to trick them, and two people behind one keyboard solving one problem and just work on the one problem together. And in the process of working this, all kinds of questions come up and instead of talking about in general things, you can talk about it concrete things that you're actually doing in a code base. So that is by far the only thing that I know that works and expected to take a long time. Yes. >> [INDSITINCT] >> HEVERY: If you--if you must go with that, yes, but sometimes it's a dirty world, especially in corporations. Yes, it is. You can actually get two keyboards. And USB keyboards, they work together and they can fight over the mouse. Yes. It's called extreme programming sometimes or agile. >> [INDISTINCT] >> HEVERY: Again, so you're asking this general open-ended question and it's very specific to what you're doing and what you're building. So what is--what we might advise is choose a framework that--where they thought about testability, right? So, what would be an example of extreme? So, for example, Ruby, and I'm not saying Ruby is the answer. I'm just saying Ruby and the Rails, they spent a lot of time making sure that they have a testability story. So Rails, I think part of the success of Rails is the very fact that not only do they give you a language in the framework but they also give you the whole methodology. So, you know, keep building the Rail's application. This is where you put the controllers. This is where you put your test. This is how you run the test. This is how the whole thing is broken down. It's all laid out for you so that you can just kind of go ahead and start it. On the other hand, if you choose a different framework--let me think of one. What was the thing popular in Java for a while, a long time ago? Struts, for example, Struts didn't ever really came with any test ability story. Like it was just a framework by itself, right? And so already, you know, it's harder for you because if that's what you're introducing to the company and not only do people have to learn Struts, but now you also have to introduce some kind of a test ability story. And given that Struts was not written with probably testability in mind, you're going to have a much harder time doing it. So, my advice is just choose pieces that have already a test ability story in there. So, I'm--my hope is that as the industry evolves, basically they realize that when frameworks are being done, the testability story of that framework really is just featured just like anything else. And so I'm hoping that when people are going to be choosing framework A over framework B, one of the features would be, "Hey, does the framework have test? What's the coverage and do they have a story for both unit testing and end to end testing?" >> [INDISTINCT] >> HEVERY: To make sure you have a testability story. Part of the framework that you are building should really be "How do I test different pieces?" Like, you should think about it from day one. It's not something that your user should really be figuring out. You need to deliver to them not just the framework but also a story of how you're going to test this thing. >> [INDISTINCT] >> HEVERY: I'm sorry. Can you speak up? I'm having a hard time--there's a--there's a microphone coming your way. >> Check. Check. Application that are crossed platform, how would you test for that if you have environments that? >> HEVERY: Cross platform, you mean like a--the thing with the multiple browsers? >> Multiple browsers or even the new application that allows you to develop one application that will work on multiple forms. >> HEVERY: Yes. I think that it really comes down to the same story. I'm sorry, I'm going to sound like a broken record, but I just--you need to have a testability story with it. So, if you have a choice within two frameworks, you know, one is promising that they have a testability story and the other one isn't, that makes--that should be part of your decision making process. And if you--if you're going to choose a framework that doesn't have it, it really should--part of the whole design process is you figure out like "How am I going to test this thing?" If it's a web browser, maybe you can use something like Selenium for end to end test and then can I write my classes for that particular framework in such a way so that there is a testing story involved in this as well? Maybe you--if you, for example, choose a framework that you absolutely have to use but it doesn't have a good testability story, for example, servlts are a perfect example of that, testing a servlet is next to impossible. And part of the reason is because the server--the method, for example, doGet or doPost takes an HTTP request and HTTP response. And then instantiating that class is next to impossible. And that requires other things. And so this [INDISTINCT] things like pulling on a sweater [INDISTINCT] never comes. I'm not threading a sweater, right? So, in that case, what you need to do is you need to build a tiny layer between yourself and the framework. It presents the testability for you. So, if you're going to--for example, the servlets are the answer, then the first thing you need to do is build a really thin layer, almost like a veneer between your code and the servlets so that your code never talks to these offending objects like HTTP request and HTTP response because those things will be closing troubles for you later on. >> Do you have any advice with regards to design, like dev data so... >> HEVERY: Dev data? >> Like development data to, like, [INDISTINCT] data? >> HEVERY: Oh, I see what you're saying. You're saying that if you want to do an end to end testing you probably want to have some data sets to kind of--yes, sorry that's kind of a little out of my--what I focus on because my focus really is unit test and you're really asking an end to end question and I don't really have an good story for you there. Sorry. >> I want to [INDISTINCT] questions about the servlets. >> HEVERY: Where is the mic? >> Is it reasonable, for example, to use stops for this? For example, [INDISTINCT] has, like, nice stops for request response. Like in--create, like, default constructor for servlets and instantiate and [INDISTINCT]. >> HEVERY: Is it reasonable to use that, right? >> Yes. >> HEVERY: Yes and no. It is reasonable because it's better than what you had, but in the long term, you don't want to be mocking out or stubbing out HTTP request and HTTP response, right, because get back to this example we had over here. If I had a user class and suppose a user asks for a cookie as the constructer and so Spring framework comes along and says, "No worries. We got a cookie; fake cookie for you that you can make." Now it becomes this troublesome thing because you got to make the fake cookie, then you have to compute the cookie because cookie is just a string. You got put the string into the cookie, instantiate the user and then hope that the user reads the cookie and then probably talks the database in order to get the user data, right? It's this convoluted way. Like, it's testable but it's not clean. So a much better design is to say, "No, no, no. User class is just a value object. It is not responsible for reading its state," right? Put that information somewhere else and then have the users simply take a string, which is the username. So now if you have a third class called authenticator and it says "I need a user," it's a piece of cake to instantiate because you just make a user, set the username and you're done. So these patches that we have that allow you to instantiate hard to test objects like servlets and cookies, et cetera, they're better than nothing but, really, they're not going to solve--save you from death by a thousand cuts because, yes, it's a little less miserable, but it's still desirable. >> Misko, we have a question over here. >> HEVERY: Yes. >> Hi. My case is a bit surprising maybe. It's I hardly have specs or requirements or cases... >> HEVERY: Yes. >> ...and my application is like a software for emergency departments in the hospitals and they used to hire like clinical nurses who have smart enough--who are smart enough technically also. So now they want an automation application, so that's why they hired me. >> HEVERY: Yes. >> So it's not exactly an automation, they want an auto-magic. So they want me to perform everything. So they hardly have specs or test cases. So, what do you suggest--which framework do you want me to follow up? Do you want me to write a test cases first or that would be [INDISTINCT] scenarios. >> HEVERY: No. You have a bootstrapping problem, right, like "How do I get myself going over here?" And in a situation like that, you know, test first has no meaning. What it really comes down to is step one, I need to figure out a way to write an automated whatever, right? And that might be a really hard thing and maybe it might be something else. If it's a web app, maybe you can look into Selenium, which means the only thing you have is end to end test. >> It's a desktop application. >> HEVERY: It's a what? >> A desktop application. >> HEVERY: So, desktop application. Then I'm sure there is some kind of robots that will pretend to be user clickers. Yes, it's a horrible way... >> Yes. I used to do the record and replay thing, but that doesn't work at all. >> HEVERY: Right. So that doesn't really work and the reason for that is because you don't want to record a replay. What you want to do is you want to build up a DSL. You want to save yourself. Like, you want to say, "Go click on patient details," right? And that method should go figure out of where the button is and do the right stuff, et cetera. It's not the best solution because, really, what you want to do is you want it to be a layer below it which is that you want to say, "Forget the UI. I'm just talking directly to the controller object and calling the [INDISTINCT] to verify that the right data is being uploaded." But give the fact that it probably was written without testability in mind, It might be the only option that you have. >> There were no tests before. It was a Fox application and they are upgrading to .net. >> HEVERY: Right. >> And it's all like cut and paste, like, patches all over. It's like hell for me. I've been working there two years. >> HEVERY: So, there is no magical--like I said earlier, like, there is no magical answer for you, right? It's just, you know, one miserable step after another, like figuring out how I can put some kind of a test framework n it, how I can build some kind of a domain-specific language around what I'm about to test because doing clicks will simply drown--yes, will down myself to early--you know, in the whole mess of things. So just abstractedly away from it and just accept the fact that this will probably never be the sexy super fast application that you cannot [INDISTINC] verify everything. But it is possible through a lot of blood and sweat to get there. >> Yes. Right now, I'm automating in the scenario base like, you know, they have discussed--you know, issuing an order, competing an order. >> HEVERY: Yes, it is important to have stories like I said earlier. >> Yes. All right, thank you. >> HEVERY: Yes. >> I was just going to say more about how I got into testing. I mean, I'm sure almost everyone in the room has done this where you're running some piece of code and you're looking at the console or you're looking at webpage and you put some sort of print line system or something like that and you're looking for a value. So, you are doing a test day, you're expecting some kind of value. So if you run that even once, so--and certainly if you run it more than once, you might as well write the same amount of code as a test where you're saying instead of expecting to see this on the screen, you just--in the test, you say, "This is the value I expect when I put this value into a method." And I got started in a very simple way just where--just testing a handful of tiny methods really simple and it's gradually built up and built up on the project I'm doing and I have hundreds of tests now. And to me, that's a much better kind of metric for how successful the project is and how much progress I've made because I've actually--each one of these tests test something real, which I really want to happen in the system. >> Yes. >> And the other thing I'd say as well is to try--when you start off this, it's quite easy to end up testing things that isn't your code. So, you end up testing libraries and things like that. So, really think about it. It's where your own logic is. It's what--what is it that your own code should be doing rather than self-testing whether it will get something out of the database because, hopefully, your URL is going to be tested. >> HEVERY: You know, that's a great story. Anybody else who has a story like that to share? >> [INDISTINCT] >> HEVERY: Yes. Do you have a story? >> I actually have a question. >> HEVERY: Yes. >> The question was do you have any text or papers or books that you would recommend that expound upon this subject well? And you're premising as I ask that. >> HEVERY: There are good books. Igor, can you suggest something? >> [INDISTINCT] >> HEVERY: Yes, I think that's for JavaScript. I mean, you were doing JavaScript. There's another one Growing Object-Oriented Software Through Test, I think it was called. There's always my blog. >> Also, another Googler, James Whittaker. >> HEVERY: James Whittaker, yes. >> He's written a couple of books. Whittaker with two Ts. >> HEVERY: That's right. >> There is one of them that [INDISTINCT]. >> HEVERY: Yes, Google's Java Reviews Guide. >> [INDISTINCT] >> HEVERY: Yes. >> And so, I personally found the Google Java Reviewer's Guide very useful, so I really recommend that one. >> HEVERY: I have this bug in my Mac that if it wakes up sometimes I don't have a keyboard. But if I put it to sleep and wake it up again, the keyboard comes back. >> [INDISTINCT] >> HEVERY: Yes. >> [INDISTINCT] I work at an agency where we do a lot of web development in the server side and front-end and their automated builds are really good at testing every time there's a check in. However, we have problems with the front end of the JavaScript code. Is there anything that you know of or Google might use to emulate a web browser or a dom or a JavaScript rendering engine so we can actually test our [INDISTINCT]? >> HEVERY: Right. So, you there's a couple of testers. You--one is something like Selenium, right, that's an end-to-end runner. What else? Well, the web driver Selenium kind of is for end to end. It depends on what you wanted to assert, right? Do you want to assert that the page looks--renders properly or do you want to assert that the--when I click a button, the right stuff happens, right? So they're different extremes over there. >> What's sort of the logic in order to have [INDISTINCT]? >> HEVERY: So, a good framework would help with rendering. Again, a little shameless plug. So, we're working on a project called Angular [INDISTINCT]. There, we focus quite a lot on testability and also making sure that rendering is easy. So, again, it's just a choice of your framework that helps a lot in that--in that situation. That's the URL and I'm not sure why my internet is not working, but that's the URL. >> Maybe you're on [INDISTINCT]. >> HEVERY: I thought I have them, but I don't know why it's not running. >> Do you want to [INDISTINCT]? >> HEVERY: Let's see this. Yes. Let's see what's going on with my network. Sorry, I'm going to try to be back in--other questions? Yes, back there. >> Sorry. I had a--a long work [INDISTINCT]. >> HEVERY: Yes. >> So, are there recommendations or [INDISTINCT] any copies in commencing communication? Just wanting--or is there a key to design [INDISTINCT] for sandboxing that--so that it doesn't impact the test? >> HEVERY: Yes. So, the general answer to that is it's always the--everybody has a, like a--you know, I showed you the super bug on the Android. Everybody's like, "Yes, my code is different. I've got this big problem." And usually what it comes down to is you're doing too many things and you need to decompose it, right? So break this down into smaller problems and then the smaller problems usually are testable. And these very specific questions, it really comes down to, you know, somebody on the team really caring about it and spending a lot of time trying to figure out like how can I break this thing down so that different concerns are separated so that I can test these things in pieces. >> So, yes, we have some--we've got a question over on the other side of the room as well. >> HEVERY: Go ahead. >> Hi. I'm a TDD new. I'm a veteran of the Cowboy Camp of Coding and I have a project up--I'm right over here. >> HEVERY: Oh. >> I've got a project upcoming which is just me and it's about two weeks long. It's an ideal candidate to sort of do this for the first time. It's in Python and I'm planning on using Jango for it. Is there anybody out here who--who's built or built a lot of TDD stuff with Jango or Python? If they could suggest maybe a quick start path, something that I could look into to actually running and learning during the course of this two-week project. >> HEVERY: Does anybody have any suggestions? I'm not a Python guy. >> [INDISTINCT] >> When I'm trying to learn a new languages or anything like that, I usually try to, like--they call them like Python Challenges where you have write a code to fix what they're looking for. You could look them up on Google, Python Challenge. They give you a scenario where you have to decode something and you had to write a Python code to fix it or to decode their code. There's a lot of them like that. Like, when I was learning about security, I went to hackthissite.org and it will show you how to write or how things are hacked so that way you know how to secure it. And they've got different levels and tip and stuff like that. I did the Python one where you had to decode a layer of coded and you have to translate it back to English to know what--how it worked and you have to use Python to fix that problem. It's called the Python Challenge. >> HEVERY: So, to add to that, this might not be obvious is that it's--a lot people when they start to do unit testing, they somehow come to it from the point of view "I got to test the whole system end to end," right? And try to give up on that idea and rather say--start small and say, "Hey, look, there's this tiny little method that almost does nothing. It sorts people by age," right? Can I write a small little test that basically asserts that that method works properly? Maybe this is part of a larger web application that allows you manipulating your address book and that's one of the features, to sort them by their birth date. And that's a method, right? So start small. Start saying, "Hey, can I write a test for such and such utility method?" And then, maybe you can grow from there and say, "Well, can I write a test to read from the database?" and "Can I write a test for a controller such that I can replace the real database with fake database?" And that really would reasonably force you to restructure the code so that you cannot decouple the controller or rather the behavior of a page from the persistent storage. All along, what you're doing is you're not really doing any kind of assertions about the UI because, as I said, UI is really difficult to test and it's hard, right? So don't try to pretend to be that you're the user and writing an end to end test. Usually, that's more than it's worth fighting for. Rather, start small and slowly build up kind of experience and get better at it and grow it. And then over time when you have this experience you'll be able to say, "Well, this is worth testing and maybe this one is more effort than it's worth where I can do some trade off." I mean, it's just a UI or something like that. Then maybe you have an end to end test framework added to the mix. So, it really is a continuous process that you just simply get better at it over time. It's not just some magical thing that just happens, right? >> You got time for maybe two more questions. >> HEVERY: Okay. >> [INDISTINCT] >> HEVERY: And as you go--and then it's all--try to push the limits and get... >> Repeat the question. >> HEVERY: Sorry. The question was, "You're saying start with the most trivial of methods and then grow from there to get methods that are more and more complicated?" and the answer is yes. That's exactly how you learn. You just get better and better at it. You know, stuff that--even the most trivial method to test would require you to have some kind of a test harness, require you to have the ability to run this thing in a continuous fashion, to run it maybe on a regular basis, to manage your test, and as the test grows, to have some kind of a strategy for it. And all of these are learning that you're going to gather as you're--as you're doing this. All of these are kind of unspoken things that are in the background that you just have to know how to manage this process. And you can learn it whether it's something complicated or something simple. So, start small and then just grow from there. So, two or more questions. I think you already asked. Wait you guys all had a--who hasn't had the chance to speak? >> [INDISTINCT] >> HEVERY: Okay, sorry. I thought you did. >> Do you have any thoughts on developer testing was just QA testing? >> HEVERY: Developer testing was just the QA testing, yes. So my thought is that developers absolutely have to write test and their job is to write a unit test. The second level would be the QA. So, there's two kinds of QA. There is a, what I would call, exploratory QA which is that you have a system and nobody really wants to be that guy who reads the script and goes clicks on it, right? You don't want to [INDISTINCT], but you don't want to have them. But it is useful to have people and especially some people are better than the others, who is really twisted about, "Hey, can I enter some crazy value into this thing?" This is exploratory testing, right? And when they discover something, ideally, you want to turn that into another story inside of your reservoir or tests. That's one kind of testing. The other kind of thing is--well, at least in Google be call SET, Software Engineering and Test. And their job is to worry more about the end to end harnesses in the framework, right? So as an engineer, as a software engineer, my responsibility is to write the unit test. I think a good engineer should also worry about how the end to end framework should work, but in this situation, it is useful to have a specialists to basically comment and say, "Okay, if you've done your unit testing story properly, then let me help you write an end to end story" and then it's everybody's responsible to add to the reservoir of the end to end test. But it's really a different kind of a thing. So that's kind of how the breakdown should happen. In theory and practice, you know, things are slightly different in all ways. And one last question? >> Thank you for the talk. My question is based on a lot of legacy [INDISTINCT] PL sequel. Do you have any advice on setting up a test harness for that and that testing that [INDISTINCT]? >> HEVERY: For PL sequel? Well, if I was faced for that, right, the first thing I would try to figure out is right set of scripts so that I can bring up a data base when it resets--and it resets into an unknown state, probably an empty state. And then, have some kind of a harness that can go and write and read into the database and then build up scenarios from there. This--what you're describing is very much an end to end test, right? And it comes down to I need to build some way of executing a set of sequels so that I can create scenarios. And once I have scenarios, I'm going to drown because it's nothing but a whole bunch of sequel, so I need some kind of a domain-specific language so I can tell stories. So, it really is kind of a progression. Can I execute sequel? If so, can I turn it into stories next level up? I think we're out of time, Howard. >> Yes, exactly. So, please--everyone, please help me thank Misko for coming out. >> HEVERY: It was a pleasure.
Info
Channel: Google TechTalks
Views: 214,648
Rating: undefined out of 5
Keywords: google tech talk, google nyc tech talk, software testing, javascript
Id: XcT4yYu_TTs
Channel Id: undefined
Length: 78min 41sec (4721 seconds)
Published: Wed Jan 26 2011
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.