 Okay. This looks good. Let me just... Okay. Hi, you guys. Hi. So my name is Gabe Hallamy. I am relatively new to Python, so I just want to start out by saying I'm probably going to say something that's wrong and if I do, let me know because that's how we all learn and I will appreciate that. A little bit about my background before I get going. I come from primarily a Ruby and JavaScript background and I'm a consultant here in Singapore. I recently rolled off a six-month Python project and TDD is a test-driven development, it's a big part of what I do and how I work and I needed to do... I needed to bring TDD to this project that we were going to work on, so the engineers knew Python, they didn't do a lot of testing. I know testing, I didn't know a lot of Python so there was kind of a nice marriage in that I got to learn the language and survey about what sorts of testing tools were out there available and what I've distilled here is the result of those six months so my tips for if you're new to testing in general you'll probably learn something if you already are doing TDD but you're not using PyTest you'll probably find something here you like if you already use PyTest and you're really comfortable with void space mock you're probably not going to learn anything and enjoy the sound of my voice so that let's get started so PyTest is a testing framework for Python you don't need to use PyTest to do test-driven development in Python you've got a unit test built in but PyTest has a lot of features that you might be interested in the way I found out about it was Cloudera they do a lot of Hadoopy open source stuff and they have a popular tool for doing SQL on Hadoopy called Impala and Impala has a big Python component and they used PyTest so the project I was on was using Impala I discovered PyTest through that and I was like oh then I found out that a lot of people use PyTest because it gives you a lot of nice things so why PyTest? it's concise it gives you helpful assertion messages there's this concept called fixtures which instead of your normal X unit sort of set up tear down stuff there's a different way they use this dependency injection to make your tests more readable if that sounds scary to you don't worry I'll explain it and it won't be scary you'll think it's cool. PyTest is popular and there's a ton of plugins so that's kind of this is what I'm going to cover probably in the next 20 minutes I hope so from PyTest's homepage this is sort of like the fuller points about why PyTest so I hope you can read it on the back but yeah it kind of runs everywhere it's got a lot of tests the testing framework of course the test itself I like that they have a strict background compatibility compatibility policy of course that's kind of the norm in the Python community which I like but it's nice that that's there they have really great documentation a lot in fact I would almost say so much that it's hard to figure it all out at first after this talk you'll understand enough of the basics that if you go and you look at the documentation you'll have an easier time navigating it but there's a lot of it there's lots of plugins a lot of people use it and of course there's a lot of examples so how do you install it? that's easy, you just did a pip install if you use pip or I suppose you can easily install it too but from what I've heard you're supposed to use pip so I use pip to install it and pip test so the weird thing is it's called pytest no. but the binary it installs is py.test so when you're invoking it on the command line you're going to type py.test but the A is called pytest no. so just remember that so our first test this is just going to come straight off of the pytest examples on their website is it okay in the back? awesome right so we have a function it takes some exit and adds one to it and we have another method here called test-answer I guess it's a function because it's not in the class but anyway it starts with test underscore it can have anything after that pytest will automatically discover anything that has test underscore as a function or method name and you'll run it so this is just a simple assert and some function equals some value obviously this won't work but if you were to write this in something like test unit it's or unit test rather it's like assert equals you know thing comma thing that's cool but what's nice about pytest is you kind of only really need to remember assert and a lot of the common things you're going to want to assert you can just assert with an expression and it'll make sense so that'll become more clear in a minute what does it look like when you run this so if you just say pytest it's going to find any files in its current directory that you run this command in it'll look through those files for your method to have test underscore and it'll run those that's the simplest indication it actually can do a little bit more things for you but it's going to be hard for you to read in the back I have a zoom in on this in a moment but you kind of get this kind of output this is the default output let me zoom in I ran this test and obviously there was an assertion error but it tries to be even more helpful and say okay so this is a line of fail and here were the values that were actually there right and so it tries to of course this is like what line number in the test the what line number in the file that it ran caused the failing test so everything you need is there by default it's quite verbose there are different output styles you can tell PyTest to do you can tell it to do the native python exception traces you can do one line per failure and there's like a middle ground between this and that called short that gives you a little bit of context but doesn't blow your scroll back with way too much helpful stuff so before we go on the easiest thing to do is to test if there's something equal to something else or not but of course you can also test exceptions really easily so this is very nice if you want to test does something raise an exception the easiest way is to use this context manager and so within the duration of this so with PyTest.raises give it the exception you just did and see if it will raise it on make your thing raise the thing and this test will pass and so it'll just say alright everything's happening I expected it to raise and it did but one of the really neat things about PyTest is the context sensitive comparisons go into this a little bit and they kind of encourage you just go look at the source for more here are my favorite examples so you can do set assertions and it's going to be really helpful right so a certain set equals this other set I put it on free lines just so that it would read nicely obviously you wouldn't normally put on free lines probably but look right extra items in the left set one extra items in the right set five that's super helpful it doesn't just go yeah those two things they weren't equal so that's nice of course for stringing quality it tries to be even more helpful and if the strings are almost the same but a little bit different you don't have to go figure out why it puts a little character it shows you exactly where they're different that works for multi-line strings too out of the box so if you've got you know a free slam bar and a free eggs bar I'll tell you right they're both a good bar but there's a diff of what was different between those two it even does really really long string differences so this is sort of a contrived example but if we have a string A that's a hundred ones and then an A and then a hundred twos and string B is a hundred ones and then a B and then a hundred twos we're turning this up to three equal it's going to say right okay I did the assertion for those of you in the back who says skipping 90 identical leading characters and diff used dash B to show if you do want to see them all and then it says both of us in this one skipping 91 identical identical trailing characters it tells you how many identical leading and how many identical trailing and then it kind of zooms into the part that's actually different and gives you that same little character it's really sweet like these the people who make pie tests have thought a lot about making your life easier and again this is all just with assert right we don't need to it's not like assert multi-line happens to be equal and assert super long lines are equal but show me the part no just a circle it isn't a dictionary so this is quite nice too it's sort of a elaboration on the set one so there's one identical item but different items right B the key B exists in both one and one and two and the other left contains C, right into D so again very helpful so context sensitive assertions next up we've got a classic X unit set up in teardown so if you come from a like classic TDD you think okay right you're gonna give me a class and inside that class called test something or other I'm gonna have a method called set up and I'm gonna have a bunch of methods called test something test something else, test something else, blah blah and I'm gonna have a method called teardown and what it'll do is it'll run my set up method first and then it'll probably usually you can decide right like do I want it to run once before everything or do I want it to run once before each of my test methods inside that class so this is really it's gonna be toward for you guys or in the back but what's going on here is PyTest supports that out of the box this is not the PyTest way but if you if you want to use PyTest with this sort of classic X unit style you can so you've got set up module, teardown module those are gonna happen at the beginning and end of whatever for each module file that PyTest finds and decides to run on then you've got inside some class you can have class methods like set up class and teardown class that's going to run once for the class but then you've got set up method teardown method that's gonna run once for each method and then inside here I just have two tests test one test two what this looks like if it runs is I just made an example that kind of showed you the nesting of it all right so there's only one module here there's only one class set up method don't run once test one then teardown one then inside the method go run again test two then teardown then teardown class module but PyTest doesn't want you to do it this way PyTest wants you to use this thing called fixtures and here's what PyTest has to say about fixtures the purpose of fixtures is to run a fixed baseline upon which test can reliably and repeatedly execute that doesn't seem like the best way to sell them to me but they also say fixtures have explicit names they're activated by declaring their use from test functions modules, classes, or whole projects this sentence here about activated by declaring their use that's dependency injection if you don't know what that is this will make sense in a minute if you use Angular, super popular in Angular you already get this so we have a classful person he's got a method name called gree he doesn't do anything but I guess I want this test to fail if I can do those I can say I've got a method called person I'm going to use this it's a decorator from Python or annotation it's decorators I'm going to use decorator here and say right this function should be considered a fixture PyTest when you run you're going to see this and know that this guy if I pass him as a parameter into any of my test functions I want you to run it and insert the result in there does that make sense so what I just want you to do is not use this setup and tear down stuff but instead declare the whole world of your test everything your test depends on right here in the method and that's super nice because you don't have all this like nesting invocations and stuff I've got a person that's all I need to think about for running this test and you don't have to scroll back up 50 lines to see what was that setup for this test so right I'll look at the result from person.gree they're the same and ah so this is actually a bug in my test but I should have said it's something like highgamer or lowgamer anyway the point is it'll fail assuming that gree was actually implemented so this is just an example of showing that it fails so how did this work right so again PyTest sees that test gree needs a function argument named person and it saw oh right there's a matching fixture called person so I'm going to run that fixture function I'm going to take the result I'm going to pass it in to the scope of that test what an excuse so fixtures are awesome a few reasons why they can call other fixtures so you can have you know because a fixture is just a function too and PyTest will say oh this function wanted person but person wanted favorite food you know or you know buffet or something and it can curl up the chain of you know dependencies that need to get injected then it'll run them all in the right order you can also use fixtures to do parameterized function testing I'll talk about that more later in a way that doesn't actually use fixtures but suffice to say that if you want to say this fixture is going to generate a bunch of different data and I want each of these results that this fixture generates to be passed one at a time against this function you can kind of do things like that right you can share them between test files even so if it's super you can do it with an import and if you don't want to import everywhere PyTest has this file called ConfTest.py that they'll look for and kind of boot into its environment before it runs if you have a feature that you use everywhere and you don't want to declare it in all of your test methods you can say auto use true as part of the decorator and that's you know of course don't abuse that but there's a time and a place where you're like right all the tests inside this file are going to use this mock something or other and I don't want to have to inject into all of my tests because I don't want to use true so that'll do the setup and tear it out implicitly and of course you can even test your fixtures because they're just functions so you can even write tests against the fixtures if you want some extra certainty about oh I'm not really sure if this fixture is doing what I think it's doing or better yet the fixture's really complicated but you have a lot of tests that depend on it why don't you write a quick unit test for your fixture and then you can refactor the fixture and be confident that you're not going to break all your tests because you've got tests for your fixture okay so that's kind of about the basics about pie tests and I want to get into a little bit about mocking so how do we verify calls this is another thing we need to do in TDA all the time or if you're doing just the driven part doesn't make sense in testing we want to verify that things are getting called so imagine we have a class called dv he's got a persist method and imagine we have a class called person and when we construct it we have a name and an instance of this dv and when we call save on the person we expect that person to call persist on its dv passing himself in it's a contrived example but I think it kind of makes sense how do we test this so I want you to use mock mock from what I understand as part of python 3 is that right if you're not using python 3 I wasn't for this project I was working on I was using 2.6 even you can believe that voidspace.co.uk slash mock I think is the URL just if you look at google for like python mock voidspace this is the one you should use part of python 3 you can use in 2 so from mock import mock capital M and from my example file I'm going to import my person in dv class so I'm going to make a fixture called mock dv and that is just going to return me a mock and I'm going to say hey mock I want you to spec yourself like this dv class and so that becomes like a verifying mock that'll say okay I'm going to record how I'm interacting with this object right it's not really a dv it's this thing that's going to have all the same function signatures as a dv but they're not going to return anything by default it's a fake implementation but if I call a method on this mock that's not in my dv this mock instance will say oh then I'm not supposed to you're interacting with me in a way that I'm not even supposed to handle so maybe you're doing something you don't want it's usually a good idea you don't have to you could just say mock with nothing and then you have basically like a null object or a ghost object where you can call food.bar.baz doesn't matter on this mock and it'll remember right food was called and food.bar was called and all that stuff but generally speaking I would advise to spec your marks when possible so that's the setup that's our fixture here's our test so test safe persistent dv I'm injecting my mock dv fixture there so I'm going to make a person and I'm going to pass that mock dv into my person right because that's how I constructed my person I'm going to say okay hey mock dv your persistent method please assert that you are called with gate does that make sense to everyone and that'll pass so there's no point in me showing you passing test output because it'll just look like a dot and it'll say test pass but let's look at a failing test so what if I inject my mock dv into this test and remember this is a different test than my previous one so there's no shared you know side effects between them and I just say hey mock dv persist assert you were called with nothing right I can just say that's basically like assert you were called it's going to blow up and it's going to say right I expected this call persist but persist wasn't called because of course nothing happened in this test I just made the assertion but that's what a failing assert called assertion looks like if you call it with a different argument so if I help persist with 1, 2, 3 and I say assert called with nothing it's going to say right I expected persist to be called with nothing but it was actually called with 1, 2, 3 so that's super helpful and that's exactly what you would expect an error message to show you but what if we have multiple multiple calls so we'll say persist 1 and persist 2 and it'll say assert any call 1 so that hopefully makes sense it's an assertion I'm saying I don't care what order it happened and just tell me were you ever called with 1 and that'll pass the gotcha is and this is what I wanted to show you, assert any call so by default assert called with not by default this is the way it works it only tracks the last call I feel like this is sort of misnamed because to me if you're like hey mock were you called with food to me when you play in English when you call with bar you need different calls that's what assert any call is for so assert called with only tracks the last call I wish it was called assert last called with there's going to be no confusion so the corollary to that is if you call it mock with bar and then you said mock assert any call foo it's just going to fail the behavior and this is a patch I'd like to write in my infinite time I would like it to say you expected to be called with foo and that wasn't true but I was called with bar that would be super helpful and it could totally do that for you but it doesn't just a warning about that I remember when Ronald was here with me and I figured that we were like damn we just lost half an hour because we were just expecting because we came from Ruby a similar type of assertion that would have said in the testing framework we're used to you asked for foo but it was actually called with bar so we had a naive assumption that of course it's so good why wouldn't it behave that way it turns out it doesn't so beware okay still being returned values so here's a class called weather service it's got a barometer and let's pretend this is a real weather service class that goes out to the internet and does something so we don't really want it doing this in our test because it's unpredictable we'd like our test to be determined this day so maybe we have this forecaster class I'm sorry if I might contrive examples so forecaster you give it an instance of weather service and then you say hey forecaster forecast and what he'll do is he'll call the weather service and he'll say hey what's the barometer at right now and then he'll just look up in a dick right if the barometer says it's rising then it's going to rain and if it's falling then it looks clear like it's not going to rain and so that's all this is doing so let's test that so we'll just get everything imported and I will make a mock weather service or you could call it web service I would never really name it mockws that's ambiguous but otherwise all my other examples in here don't fit on one line so forgive the short end so mock weather service mockws is going to return a mock of a thing that looks like a weather service so then here we can say forecaster right in my test with my fixed shirt make me a forecaster using mock weather service and then here we can say okay mark weather service when somebody calls barometer the return value should be rising and then I will call forecast on my forecaster and see that it's going to rain and do the same thing where when the barometer is falling but it looks clear so return value is the thing you can set on any mock and when that mock is called it will put that in as a return value so that's cool but we can make this a little bit simpler I think and this is this parameterizing I was telling you about before so you can say okay I want to mark this test as parameterized and I want to call it reading and I want to say expected forecast when it's rising it's going to rain and when it's falling it looks clear and what that looks like here is it's going to say right I have a single reading this thing called expected forecast and those are going to be 1, 2 that's reading that's expected forecast you give it however many arguments you need in this sort of tuple format and it injects them in one at a time into your test and it's perfect for this kind of case right you combine all of your very similar tests when the only thing that's different between your tests is the input and the output in one really concise way so I think that's pretty neat okay monkey patching the other thing I want to explain so mock has something called patch and it's very similar to something that pie test gives you called monkey patch patching with mock is done typically through a context manager those are great you should use them of course but I find that I don't like the level of indentation that it gives me for the rest of my damn test when I'm just patching at the very beginning so when I can I prefer to use pie test monkey patch which is a very similar thing what it's going to do is it's going to go in there and let us mess with something because what if I can't mock and inject it into my constructor for some reason what if I've got the same class called weather service weather service has a gromder let's say that I'm pretty cool stuff that's fine but for temporary forecast this is the same as before for temporary forecast a class doesn't take the weather service as an injectable parameter to its constructor he just instantiates a new weather service because he imported weather service in his module how do I test that? and that's where the monkey patch you can commit so monkey patch we say set adder and it's going to go into a module whatever path you give it as a string and it removes the patch when the function returns so it's only patching it for the duration of each of your test functions so if we use it it looks like this so we're passing monkey patch it's a fixture it's just a fixture there are a couple other fixtures I don't have time to talk about the monkey patch is just a fixture in pie test so we've got monkey patch we've got our mock w.s. from before the same mock weather service so here we go this is where things get also a little bit tricky I have this big w.s. thing which is a mock a generic mock where the return value is my mock weather service so this is like the class and this is like the instance of the class does that make sense? so that's why I use capitals for it too so I can say ok monkey patch I want you to set in the forecaster top weather service module namespace I don't know the right python word but go in there and so when my module runs he's already importing this thing modules are just singletons and pythons so it's going to go right I'm going to go in for the duration of the test I'm going to take weather service's value instead of making this mock when you instantiate it I'm going to give back my mock web service instance and then it's the same as before I make my forecaster I can inject it in and it makes the same good? cool ok plugins I want to sort of end with this that's all the plugins for pytest I'm going to tell you just about one of them because that's all the time we have my favorite it's called pytest ipdb of course we probably all like ipython I learned to like it more than regular python interactive shell so of course there's pdb and you're probably all used to that so out of the box pretend I haven't told you about ipdb for a minute out of the box pytest has a great feature if you put dash dash pdb at the end of your invocation to run pytest it will drop you into a debugger after each fail you're at the line that failed so it finds a failed assertion it stops right there in a debugger so you can interactively explore things and figure out what was wrong with your assertion then you can continue and go on to the next one that's kind of annoying because what if you have like 100 tests and you don't want to continue so combine with dash x dash x means pytest stop after the first failure so you mix those two things together and it's the first time I have a failure in my test suite I want you to drop into a debugger when I'm done with that we're done right let me fix that test and we'll repeat that cycle that's super helpful because it's just I know it's like a little thing but it saves you I mean go to your test and import and do ipdb set trace this just does that for you super good so that's what pytest ipdb does is instead of doing dash dash pdb you can do dash dash ipdb and it's the same thing except you get the ipython that you know a lot so you just have to install it from this git repo and the neat thing is that's all you do so pytest will look in your current python environment or virtual environment right and it'll say what python what pytest plugins are installed and I'm just going to use it so there's no like going afterwards and configuring pytest to teach it about all the plugins you installed if they're there if the eggs are there in your environment pytest will use it and that's all I have so do we have any questions Bjorn curious about the parameterized texture do we look for key arguments as well you know I don't know probably no I think it features it's a good question any other questions we don't have something for you we want the database let's try it how does it know I guess yeah but if it weren't if it weren't it's it's not going to return anything when I mock it the mock goes in there and says I am the implementation now the only thing I do is count how I was called and keep track of them if you want me to return something you you better give me a return value but mock also has a feature where you can say call through so that's also available but you can tell mock also actually I want you to go ahead and do the call this is why I thought that would be a good example for showing and testing the database in my unit test I don't want to touch the database so right so typically what you'll do is in your mock you won't just say here's persist persist is now a mock and it returns true because all my subsequent code expects persist to return true because it's like oh the persist succeeded so I'm going to do the rest of my work anything else we see the test definitely fixed so there is a test getting a fixture so you say it will search for a function called mockDB yes it's not just a function, it's a function that was also decorated yes or comf test.py so this bullet point here if you don't want to import them all the time you have two options you either put them in comf test and pytest picks them up automatically everywhere because they're super common and you want them there or you put them inside domain specific subset files and import them when needed depends on what you're trying to do but if they're super super common put them in comf test that's what pytest wants you to do so I'm going to have one central comf test yes you could do that if you wanted to I suspect that would work okay I think I should stop probably longer than I promised I would be so thank you I'm Gabe, that's Neon Twitter and that's my email address I'll end by just saying like ThoughtWorks I work for Neo, we're another agile consultancy here in Singapore we are also always looking for talented people so if you'd like to know more about Neo, you can talk to myself or Michael who also works there or Rahul Bjorn is contracting for us right now so he could kind of half the time and that's it, thank you