Slide 1

Slide 1 text

Functional Testing Tuesday, 28 May, 13

Slide 2

Slide 2 text

Testing? Tuesday, 28 May, 13 When we think of testing, it’s the easiest to grasp when talking about things like models. This is just a single item to test, and all the side effects are locked within that one item.

Slide 3

Slide 3 text

:message << Result >> Magic! Model Tuesday, 28 May, 13 This is normally done by taking our model and sending it messages. Based on those messages, we’ll either get a result, or change the state of the object. From a consumer perspective we don’t really care about the innards of the function, just the result. As far as we are concerned, everything in the model is magic.

Slide 4

Slide 4 text

Controller Tests? Tuesday, 28 May, 13 But when it comes to controller tests, we let stuff slide a little. Why is that? What makes testing controllers so much harder?

Slide 5

Slide 5 text

¯\(°_o)/¯ Tuesday, 28 May, 13 Sometimes it’s simply “I don’t know” or because we’ve never really done it before But, let’s take a minute and think about this.

Slide 6

Slide 6 text

Break it down? Tuesday, 28 May, 13 How can we break down a controller into easy to test pieces? Let’s look at how a single request interacts with a controller

Slide 7

Slide 7 text

:message << Result >> Magic! Model Controller HTTP Response HTTP Request Tuesday, 28 May, 13 If we think about it, our HTTP Request is just another message being sent to an object (our controller). The resulting object is the response, and there is a good chance there’ll be some state changed along the way too.

Slide 8

Slide 8 text

Interrogation Tuesday, 28 May, 13 So we can really just consider our tests as a bunch of questions we are sending to our controller. What do you do when I send you this kind of HTTP request? What’s your response? Did you change anything on the server? That kinda stuff.

Slide 9

Slide 9 text

Rails Tests Tuesday, 28 May, 13

Slide 10

Slide 10 text

Tuesday, 28 May, 13

Slide 11

Slide 11 text

Controller Actions Tuesday, 28 May, 13

Slide 12

Slide 12 text

Tuesday, 28 May, 13

Slide 13

Slide 13 text

Tuesday, 28 May, 13

Slide 14

Slide 14 text

Tuesday, 28 May, 13

Slide 15

Slide 15 text

Start Small Tuesday, 28 May, 13 But first, let’s start small. We aren’t going to dig into the codebase of some goliath of a codebase. We’ll do this in tiny bits.

Slide 16

Slide 16 text

Pantry Tuesday, 28 May, 13 The app we are going to be building is a pretty simple one. We want to be able to add groceries and stuff to a list. We’ll call it Pantry.

Slide 17

Slide 17 text

No Model Tests Tuesday, 28 May, 13 Now, let’s get this out of the way first. To start, we aren’t going to be writing any model tests!

Slide 18

Slide 18 text

OH MY GOODNESS GRACIOUS!! Tuesday, 28 May, 13 Is probably what you’re thinking. But let me show you the model we have.

Slide 19

Slide 19 text

Tuesday, 28 May, 13 That it! What is there to test?

Slide 20

Slide 20 text

Tuesday, 28 May, 13 So seriously, if we had to go in and test it, we’d be testing our ORM which in this case is ActiveRecord. A simple rule of thumb for testing is “Don’t test what you don’t own”. And as far as I’m concerned we don’t own AR.

Slide 21

Slide 21 text

Spec? Tuesday, 28 May, 13 So what’s the specification of our application. For now, let’s just assume we have a list of ingredients and we really just want to be able provide that to the person using the application.

Slide 22

Slide 22 text

Easy Tuesday, 28 May, 13 Easy Peasy right? It’s just a single variable

Slide 23

Slide 23 text

Test First! Tuesday, 28 May, 13 No no no fine people. We’ve gotta start writing out our tests. It’s that kind of attitude that’ll get you stuck in the controller hell you’ve possibly experienced in the past.

Slide 24

Slide 24 text

/ingredients Tuesday, 28 May, 13 So, let’s assume the route is all good to go and we have our templates all set up. All that is missing is the actual work on the controller side.

Slide 25

Slide 25 text

Tuesday, 28 May, 13 So we write up our first test. It’s pretty simple stuff. We just want to get the index and verify that a variable named ingredients was set.

Slide 26

Slide 26 text

FAIL! Tuesday, 28 May, 13 Of course, since we are doing this right, the test fails wonderfully since we are missing our implementation. Yay, that’s pretty awesome.

Slide 27

Slide 27 text

Tuesday, 28 May, 13 Now for the implementation. That’s it. We assign the variable and respond with it. Done like dinner.

Slide 28

Slide 28 text

PASS! Tuesday, 28 May, 13 And of course our tests pass.

Slide 29

Slide 29 text

/ingredients/:id Tuesday, 28 May, 13 So the same thing can pretty much be said about the show action for an ingredient. Because of that I’m going to skip it.

Slide 30

Slide 30 text

BAM! Tuesday, 28 May, 13 So let’s step this up a notch. We have our read actions, and they are pretty simple. But why don’t we look into a create action? But not just a simple “an object was created action”

Slide 31

Slide 31 text

Tuesday, 28 May, 13 Here’s our form that we are assuming some badass designer has written up. The user is going to fill in some form and when they submit it’s going to hit our controller. We do our Magic, then redirect the user back to the index along with a flash message letting the user know their item was added

Slide 32

Slide 32 text

Tuesday, 28 May, 13 So here’s our test. It’s pretty straight forward. Now let’s get that controller action actually written up.

Slide 33

Slide 33 text

Tuesday, 28 May, 13 As you saw in the test, we are able to get access to the session’s flash so we can verify what the output was. In here we make that new ingredient then set the :notice based on that. Finally redirect the user. Easy stuff.

Slide 34

Slide 34 text

Tuesday, 28 May, 13 But... something isn’t quite right. Let’s go back to that test and look it over.

Slide 35

Slide 35 text

Tuesday, 28 May, 13 That message is pretty awesome, but will probably make someone angry. Let’s fix that up but where to put it?

Slide 36

Slide 36 text

ingredient.rb Tuesday, 28 May, 13 After a bit of though I figured, let’s just put it on the model. There is a chance we might need to get the plural for an ingredient again, and this will help keep us from duplicating ourselves, which is an added bonus.

Slide 37

Slide 37 text

Tuesday, 28 May, 13 So we throw some simple tests for our ingredient. We just want to be sure we cover those “edge cases”

Slide 38

Slide 38 text

Tuesday, 28 May, 13 And then we put together our implementation.

Slide 39

Slide 39 text

Ship It! Tuesday, 28 May, 13 And version 1 is done. It’s shipped. Awesome, let’s have celebrate.

Slide 40

Slide 40 text

Feature Request Tuesday, 28 May, 13 Celebration over. Some new features have been requested.

Slide 41

Slide 41 text

Case Insensitivity Tuesday, 28 May, 13 The first one is there is a bug/missing feature that if I add the same ingredient but spelled in a different case combination that a new item is made. That’s silly. Let’s fix that.

Slide 42

Slide 42 text

Tuesday, 28 May, 13 So we go into our ingredient test and add some more tests for handling case sensitivity

Slide 43

Slide 43 text

Tuesday, 28 May, 13 Then we add it as a scope to our model. If you are unfamiliar, scopes add a way of making it easier to do searches since it keeps duplication down and gives you a bit more context. Because it can take a closure, you can even make it require variables

Slide 44

Slide 44 text

Merging Tuesday, 28 May, 13 Now the second feature is that we want to be able to merge items when we are creating them. So if I had 4 apples in the fridge and bought another 4, I should have 8 apples in the fridge. Not 2 sets of 4 apples.

Slide 45

Slide 45 text

Tuesday, 28 May, 13 So here we have our functional tests for the merging of items and the merging of items with different case. Now let’s go to the implementation.

Slide 46

Slide 46 text

Tuesday, 28 May, 13 So what we want to do is find the ingredient or initialize it based on the insensitive value of it’s name. Then we do a bunch of assignment and stuff. Increment the amount by the amount that was passed in. To keep our message correct, we create a temporary object and use it’s pluralized value for the flash. Then we respond_with our object.

Slide 47

Slide 47 text

OMGWTFBBQ?!?!? Tuesday, 28 May, 13 Now you are probably looking at that and thinking. Woah that’s kinda gross and out of control. There’s so much going on there. Yeah there is, and.

Slide 48

Slide 48 text

Tuesday, 28 May, 13 We’ll leave fixing that up as an exercise for the reader. In honesty, all that we really need is a kind of Service object that goes in and does all the gross bits for us. We can then pass it messages and all that stuff, for the things we actually care about. That object, would awesomely enough end up being a plain old ruby object, that we would also call a model.

Slide 49

Slide 49 text

Hints Tuesday, 28 May, 13

Slide 50

Slide 50 text

? Tuesday, 28 May, 13 So what does Shopify use for testing?

Slide 51

Slide 51 text

Minitest Tuesday, 28 May, 13 Well up until recently we were using Test::Unit but we’ve moved over to minitest. It’s built into the standard library of Ruby 1.9.3 (and 2.0 I believe) and is also amazingly fast. Some other nice features, that we aren’t using though are it’s spec DSL that you can use, as well as a Mocking and Stubbing framework. If you haven’t, definitely check out the source code for MT 5.0.0, it’s an easy read and shows you how simple a testing framework can be.

Slide 52

Slide 52 text

Mocha Tuesday, 28 May, 13 For stubbing and mocking we use Mocha made by the chaps over at Freerange. I’ve used the MT mocking and stubbing framework, and haven’t been wholly impressed by it. Mocha makes stubs and expectations much easier and is an easy tool to use. It does have a bit of magic though, so if that’s not your thing, then you might want to avoid this one.

Slide 53

Slide 53 text

Rails Parallel Tuesday, 28 May, 13 At Shopify we were having some serious problems running our Unit Tests in any meaningful timeframe. This is partly because our tests are slow, but also because our test suite has gotten massive. This tool helps bring down the runtime of our tests by a drastic amount and takes advantage of all those cores we have.

Slide 54

Slide 54 text

Timecop Tuesday, 28 May, 13 A lot of what we do involves time. Placing orders, dealing with subscriptions, billing, credit card authorizations. All of this stuff is really time dependent, and of course, things break come the time changes. In an effort to help reduce that we’ve started using a tool called Timecop. It allows us to jump around in time, and freeze time such that we know exactly what to expect. It’s really helped out with our tests!

Slide 55

Slide 55 text

Fakeweb Tuesday, 28 May, 13 Finally, we deal with a lot of 3rd party APIs. And being 3rd parties we can’t always rely on them, because that would cause our tests to break every once in a while when a Payment Provider decides to do maintenance on one of their integration servers or their service goes down entirely. In order to get around that, we use fakeweb to replay API responses from various 3rd parties, so we can keep our test suite running fast and stable. That doesn’t mean we don’t test our integrations. We still do, it’s just that we run them as “remote” tests so that when they fail it’s not such a big deal.

Slide 56

Slide 56 text

Questions? Tuesday, 28 May, 13 And with that being said, I’d like to open the floor to questions