Sessions is temporarily moving to YouTube, check out all our new videos here.

Driving Design with PhpSpec

Ciaran McNulty speaking at PHP Warwickshire in March, 2017
709Views
 
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

Is a SpecBDD tool the same as a TDD tool, or something quite different? This talk will answer these questions, and show how PhpSpec can be integrated into your development workflow to drive quality in your Object Oriented design.


Transcript


- Thanks for the invitation to speak here, it's nice to go to an event in the midlands. I'm Ciaran, I'm from Starbridge. Has anyone used PhpSpec? Good, because this is an introduction talk. So those two might be bored. Is anyone doing test driven development? - Ish. - Ish. It's TDD but write the test afterwards. My country. -combination. - Yeah. So I'm gonna talk about this tool I maintain called PhpSpec. Try and talk about what it's for, and we'll touch on some subjects to do with TDD and BDD. And a bit about the TDD cycle. I found when I'm showing people this tool, it's most effective when we're sort of pair programming, so I try to make this talk feel like interactive talk. So there's some code examples, we're gonna work through something as in a TDD cycle and see how the tool supports that. So first off if you read about PhpSpec, you'll see it's referred to as a BDD tool. Which is related to TDD and it's worth talking about the differences between them. So BDD, if you ask Dan North who came up with the term what BDD is, it's a second generation outside-in-pull based multiple stakeholder, I couldn't fit it on one slide, multiple-scale, high automation, agile methodology. Which is actually a very good definition of BDD, but it's maybe a bit complicated. All those things are true. Liz Keogh came up with a better definition of behaviour driven development, she said it's when you use examples in a conversation to illustrate behaviour. So that's quite a natural thing, when you're trying to talk about what the system should do, a really easy way to help people understand what the system should is to just give an example, in this case this is what should happen. And BDD, really if you reduce it a lot is about deliberately introducing examples into conversations and deliberately saying as I'm explaining how the system behaves, I'm gonna do that by giving examples of what it will do in different situations. Cool. This is, turns out, it's kind of the same thing as TDD. So it came from TDD, that's important to talk about. So people like Kent Beck came up with test driven development, and I think, was it Kent Beck or Robert Martin came up with the rules of TDD? And it's pretty simple, there's three steps in test driven development, you start by writing a test that fails. Anyone familiar with that? Anyone doing that? Some people. That's the hard bit, you start by writing a test that later will tell you wasn't the correct code. And then after you've written the code, in some automated way you find out if it passed the test. Dan North was in a kind of coaching role, training role, trying to get people up to speed on this TDD thing, TDD is very successful. It changed my life as a developer so you should give it some time. And Dan's trying to teach people this, you start by writing a test that fails and then you write code that passes the test and then once it's passing, once it's passing you then take time to think how you're going to make it better without breaking it. Which is what we call refactoring. If you say you're doing refactoring but you don't have tests you're not doing refactoring. Myron Fowler wrote a book called Refactoring where he defined what refactoring was in Chapter Two, he's like you have to have tests, everyone ignores that. You need to know you're not breaking as you're making these changes, you need to have this safety net, it works, and then I make a little change and it still works and anywhere you really get that confidence is due to having a testing tool telling you everything's still fine. So this works, it works really effectively. Makes you a better developer, blah, blah, blah. So when Dan was teaching this, there was a sort of stumbling block which is that you say to people you have to write to test first. And like I saw a few faces when I said that, it doesn't sound right, the word doesn't sound right. It doesn't sound like a natural thing to do, write a test first. Because in life, we go to school and we take a test. Or in a factory you build something and then you test it, so it feels like test is the thing that happens later. And Dan was really kind of aware of this stuff, he's into neurolinguistic programing, things like that, so he changed the words, and this was the start of behaviour driven development. So BDD, they're doing the same cycle, you're doing the same things but you're calling them different things. And so maybe you're thinking about them differently. So the biggest shift in BDD is that you don't think of it as writing a test first, you think about it as specifying it first. So the idea of a BDD spec and a TDD test are really aligned concepts. In behaviour driven development, before you write the code, you describe using examples what the behaviour of that piece of code will be, and we'll see an example. And that feels more naturally, before I start, I'm gonna think about what it should do and I'm going to describe it. That description is gonna be some code that I type in to a testing framework, but I'm thinking of it as a description of the way the system's going to behave. And then I implement that behaviour and then I make it better so that's BDD. So effectively the thing you're doing is the same as TDD, but you're thinking about it a bit differently, you're thinking more about, instead of thinking about test, which is maybe less natural to think about afterwards, I'm thinking about it as a specification which is natural to think about as something you do first. It's natural to think of the specification as my starting point. Even if it's a quick specification for the next bit I'm doing, because we don't do waterfall projects anymore, right? Right? - Right, yeah. - Yeah. - Yeah. - So you're not specifying the entire thing and then trying to write the code, you're saying well what's the next thing I need to do? I should maybe think what's the next behaviour and write capture that somehow in a way that's going to act as a test later. So to think about the landscape of testing tools in php, why bother having PhpSpec? Because there's a testing tool that everyone uses anyway. But we can kind of plot them on different axes, so, the Y axis here is, what level are you applying your tests? Are you applying your tests one object at a time? Down at the bottom here, or are you exercising your entire system as part of the test? Because we can write a test that just instantiates the class and does stuff with it, or you can write a test that kind of deploys your system to a bunch of containers and hooks them all up together and then does web requests and provides a test database, that's the entire system. And the other axis is, are they more about testing? Or more sort of BDDish tools about describing? Based off, the way the tools API are expressed, are they about tests? Or are they trying to be descriptive and be about specs? And this isn't the official logo for PHPUnit, there is no official logo,told me of, there's no official logo for PHPUnit yet. But someone did this logo and it's really popular, so everyone thinks it's the real logo. So I'm too lazy to change my slide. I started using PHPUnit around 2005, completely changed the way I approach code. Not overnight, took me a few years to learn how to do it, but, you can use PHPUnit for testing your classes, you can also use it for exercising your whole system by driving something like Selenium, you can use it for all the in between levels of testing. So if you want to learn a testing tool that you can use at all these different layers, learn PHPUnit. And to be honest, if you're a PHP developer and you're gonna work on projects, you should probably learn PHPUnit. Because 90% of projects, at least, that do testing are using PHPUnit. Because it has to address all these different types of testing, PHPUnit's got loads of utilities for doing stuff like loading database fixtures before the test, or that's part of DBUnit but it's kind of integrated with PHPUnit. Stuff like partial knocking because as an author, or as maintainers, you don't really know what kind of testing you're gonna use it for. When it was written, it was written, wow, 15 years ago? Which is amazing for a piece of software to still be in use, and still be really good, and have new versions with new features. It's about testing, so I would say most people use PHPUnit using it to test stuff after they write the code. Which isn't a bad thing, sometimes that's what you have to do. So on this side of the axis, there's some other tools, I use behat a lot. Behat's mostly aimed, behat's not mostly aimed at exercising your entire system, although that's what most people use it for. Say it this middle ground of exercising your systems application layer and driving those tests from business use cases, this isn't a behat talk though. And behat's very much a BDD tool, it's all about having a conversation with someone about how the system should work ahead of time, and then exercising some parts of the system to check it, can fulfil those use cases that the user needs the system to be able to do. And PhpSpec's down this end, PhpSpec is just for testing classes. Individually, in isolation. And that gives us some focus. While PHPUnit doesn't know what kind of test you're gonna write and so it has to have a more generic API, we can focus a bit and say this is just an API for testing classes and we can not build in features for loading a database fixtures because it's just for testing classes in isolation quickly and then throwing them away. And because we're from this sort of BDD tradition, we're trying to make it so you can read this specification, and as a human who understands PHP, you can read the specification and kind of understand what is it this class is supposed to do. So we designed the API to try to make it readable and less about validation, it's more about this is what the object should do, less about I'm gonna test this, this is the case. That might all be a bit abstract so we'll get more specific. So PhpSpec was started by Travis Swicegood and Padraic Brady in 2007, and it was inspired very heavily by a tool called Rspec, a lot of BDD practitioners started off in Ruby. The early cool BDD tools got developed in the Ruby community around the same kind of time that Rails was getting trendy and Ruby was the future if you remember. And this book's really good, all code examples are in Ruby but it's a really good sort of BDD testing book. If you want to check it out. So Padraic and Travis started Rspec, it never got to a 1.0 release, it was very much sort of patterned after, sorry they started PhpSpec, it's very much patterned after Rspec and some of the stuff felt a bit Rubyish. I'm not going to say it was a port of Rspec but it was very close to how Rspec behaves. Now my friend and colleague Marcello became lead maintainer. Because if you know Travis and Padraic, they're involved in loads of open source projects, made arguably too many, and they didn't have time to carry this thing on so Marcello got involved, released PhpSpec 1.0 and this is when I sort of started to become aware of the project. Then at some point, Marcello and Konstantin, Konstantin who wrote Behat, they decided to write version 2 as a complete ground up rewrite and they addressed some of the problems with version 1. So there wasn't any backward compatibility between 1 and 2, it was a new project effectively. Marcello and Konstantin got it as far as beta versions for version 2 and then it stayed in that state for about a year, at which point I was using it, people in Enveeker where I work, we were using it on projects successfully, tagging it against devmaster. I started to get pissed off that there wasn't a release. So that's how I got into open source. Just driven by there not being a release recently, I started contributing. Closing off bugs, helping make some new features, and we sort of got it over the PhpSpec 2.0 release. And because I was doing so much work, I ended up taking over the project. So what was the point of PhpSpec? It was optimise the tests to work as descriptions of the behaviour. So optimise the tests for readability, optimise the API to be concise and make it clear what you're testing. To encourage good design, and this is probably the toughest thing, we omit any features that help you test what we think is a bad design. So it's hard to test, if you make it hard to test bad code with your tool, it means if people are doing tests first, they can't write bad code. However this means it's not the ideal thing to apply to your legacy classes. If you're on a legacy project and you're writing new code you can use PhpSpec, but like bolting it on retrospectively to your old classes, that kind of thing, it's not gonna work. We want to encourage the TDD cycle, so we want to make it easier for you to write the test first than it is for you to write the test afterwards. So this means you have a bunch of convenience stuff that means if you write the test first, it's gonna be easier it's just to kind of brainwashing you into doing TDD. So that's the shiny stuff we'll show that looks handy, then sometime later you realise you're always writing the test first, we've got you. So the way we enforce that is by using it constantly for everything, and then finding the bits in my workflow and other people's workflows that feel like they're a bit clunky, and thinking how could a tool help you with that? How can a tool make that workflow smoother? And to address some of the issues with PhpSpec 1, we wanted it more in a php paradigm, less Rubyish, make it something that looks like PHP, conforms of a PHP developer's right code. Well, good PHP developers. So since I took over the project for version 2, I actually took over after version 2. It's, the early work on version 2 is just Marcello and Konstantin, working in private, they dumped it into repository, since then it's really been a community effort, I'm maintaining it, but loads of people contribute and if you start using it, please contribute as well, because I'm not that, I'm quite lazy, so it's amazing to just be able to merge people's pool requests after a bit of review instead of having to build all the features myself. So let's get into it, you instal it with Composer, we're on version 3 now. All we did really with version 2 was drop some depth coded things and bump the php version requirements. And this should be all the configuration you need to get started if you're using PSR-0. PhpSpec will read the Composer autoloader and use that to figure out where all your classes are gonna be so you shouldn't need any extra config, you can just start. Psr 4 is a bit more complicated. We're trying to figure out how to auto detect Psr 4 root locations. Someone's working on that right now. So we start with some top level component, we need to say hello to people when they come to our website, because the marketing team think that will improve conversions, if you get a nice greeting. And the thing we need to do is, we're gonna have to make some objects to achieve this. So we're gonna describe what the object should do. So it's like the TDD cycle, we're gonna start with the description. And we describe it using a specification, a specification is a php class that contains examples, methods called examples, in each example is meant to say in this case, this is what the object will do. In this scenario this is what the object will do. So obviously that maps to a test case and test methods. We're trying to say so for example in this situation this is how our object will behave. And we can start by using the command PhpSpec describe and then the name of the class. So we need to call the class something. So I just name the class, I can use namespaces, whatever. And in this project I've just got the Composer JSON I showed you, it's installing PhpSpec, and nothing else yet. I've got spec in a source folder with nothing in. So when I say describe greeter, PhpSpec generates a new specification for me. So if I look in the spec folder, come on I didn't ask for that, if I look in the spec folder, there's a greeter spec that describes what the class we're gonna write, how it should behave. So any behaviour we have by default is it's initializable and it should have a type. That's a good starting behaviour for a class, right? It exists. Okay. So the next step is I need to check if the specification matches reality, so for that I use the command PhpSpec run. What do you think will happen? You'll fail, right? Because the thing doesn't exist, yeah. Said it fails in a specific way, I'll read out what it says. One example, one broken, so it's not actually a failure, broken means something went wrong while trying to execute this. I tried to run the thing you described but some php thing broke, and what broke is the class doesn't exist. So we've tried to optimise this workflow, because how many ways are there of fixing class not existing? There's one. So any time when there's only one way of doing it, probably some computer should do it for you. So it says do you want me to create phpWorksGreeter for you? I can say yes. The class greeter has been created at this path, and then it runs the test again and this time it's green, because the only behaviour is the class exists and is instantiatable. And in the source folder, there's a greeter, which, is just a class. Ignore the final thing, that's my personal template. You can template it. So that's pretty simple, so I describe what the behaviour is, the tool checks if it's true, tests pass, green now, so my next thing is to describe how that class should behave. No one's gonna crash, that's good. Did that. So now we're gonna have to come up with some real behaviour. So you give an example, an example is in a particular situation this is what should happen. And you can talk to your pairing partner if you have one of those. What's the first thing it should do? This is a point with TDD, we're gonna break down solving a complex problem into small steps. So what's the next small step we're gonna take? When it greets someone it should return "Hello". In TDD would say oh we have to write what failing test should we write next? Which is harder to think about. In this BDD cycle, it's more sort of what's the next thing it needs to be able to do? When it greets it should return "Hello". So open my specification. Got that one example about it being initializable. So I have to describe that when I greet someone, it should say "Hello". It says hello. This in a spec refers to the object we're describing. So on a technical level, forget about that a second, on a technical level what happens is the spec proxies the calls through to the real object and then checks what comes back. When you're writing it as a description we're using the fact that there's a keyword called this in PhP and saying this object we're talking about, let's describe it's behaviour. So this greet shouldReturn 'Hello'. See how close that was to the sentence? We're trying to make this API quite expressive and easy to understand. Don't worry about my funny font that does the arrows. So what's gonna happen when I run it? - Fail. - Why would it fail? - There's no greeter. - There's no greet, right. Says it's broken, so the one example passed, one example was broken. Because the function didn't exist. Do you want me to create a method called greet for you? Yes. So now it's red, red means fail, so it didn't break. But the error is I expected hello and I got null instead. And it's because the class, yeah the tool can generate the method for you but some logic is more complicated. There's actually a way, there's a flag I can pass called fake just for this case, or you can turn it on in your own personal preferences. Where in these cases it will prompt and say do you want me to make the greet method always return hello? I'm gonna say yep. And then all the tests pass. So that's, not everyone likes that, it only does it when the method's empty, so you can turn that on if you like that cycle, and the point is, by writing the test first it's been easier for me to write this class, I haven't had to do anything yet. This is just, it's really good for new people to get them into the TDD cycle. You might feel it's gimmicky, but when you're using it all the time, I haven't made a class, I haven't typed a class whatever for ages. That's not the point of the tool, it's just a way to get people into this TDD cycle, the point of the tool is this expressive syntax we're using to describe behaviour. And trying to optimise the workflow. Yep, done that. So how do we describe values? You saw should return, I called a method and I said it should return, and matchers, if you've used PHPUnit they're a bit like insertions. So the way you use them is you call a method and then you say something about what the result of this method should be. So this should return hello, the sum of 3 plus 3 should equal 6. GetEmail should return some sort of email object. GetSlug should match this regx, getNames should return an array that contains this value. It's a bunch of built in assertions. The opposite works in each case, so you say should not return, should not equal, should not have time, that kind of thing. And extensions can add extra assertions pretty easily, extra matches. There's a couple of special ones, so if you say should be something, it looks for a method called is something on the object and checks it returns true. Matches because that's a pattern that loads with php developers seem to use is a common naming thing, so, should be seems to work, and I don't say should have, if you say should have something, it'll look for has something method and check it returns the right value. They're optional of course. And you can also do find your own matchers, so, in this case I'm getting some JSON and I'm saying it should have a JSON key called username. I can pretty easily define a callback that does that assertion for me. Can do that inline in the example, so inline in the spec. I can also write matcher objects and have it in my projects and just sort of reference them in a config file and they get picked up. So there's something you're checking a lot, it's quite easy to add a new matcher. So that's the testing objects bit, testing objects on their own bit, but the most important thing about objects really is that they talk to other objects and in case that's the, he regretted calling it object oriented programming, because people think it's about the objects. It's actually about the messages between the objects and the way the objects talk to each other, that's the important bit. So we have, we want to be able to describe how one object speaks to another. Says come up with another example, when I greet someone called Bob, it should say Hello Bob. Makes sense, right? So we started with a simple case, that's important. Started with a simple case where it's just saying hello, and now I'm trying to come up with a more complicated case. You shouldn't start with the most complex case, because then, your test that triggers the most complex case you're gonna have to write code that solves the entire problem and that might take a long time. You start by solving the simple cases. You'll find when you have to solve the complex version, it's all done for you, half of it's done for you already. So when it greets Bob it should return Bob. And the interaction between our object and the person is we're gonna ask the person what their name is. So our greeter's gonna say hey, what's your name? This is called a query. There's roughly two ways objects can interact, they can either tell another object to do something, Persist this to the database. Approve this invoice. Commands when I don't really expect anything back. Or queries where they go hey give me this data. So I start with queries. My greeter's gonna ask the user what it's name is so that it can say hello. And so we use what's called a stub, a stub will use the method willReturn. So I somehow have to describe how it's gonna interact with a person. What should I call it? The example? Yeah. It_says_hello_by_name, something like that. So this greet, in this case, it's gonna say hello Ciaran. But that doesn't look right, why would it say Ciaran instead of someone else? We have to pass in the person that you're gonna say hello to so I'm gonna have, like when you greet this person, you're gonna say hello. So now I need to think, well okay, there's gonna be a person object. The way we ask PhpSpec to produce one is just by typing into. Put a nice namespace on it. So when I greet a person, it's gonna say Hello Ciaran. So why Ciaran instead of something else? I have to tell the testing tool this is someone called Ciaran. So I have to set this stub up, person, getName, I hate getters, but, you know. WillReturn Ciaran. So trying readable because a person when you ask the person for their name they're gonna say their name is Ciaran. It's just kind of the preamble to the spec. And the spec is when I greet this guy it's gonna say hello Ciaran. Make sense? Kind of readable. So what's gonna happen when I run it any bits? Break why? There's no person, right? Yeah, this is good, we're kind of gonna figure out what person looks like by talking about how someone else is going to use it. So yeah, broken, purple. Do you want me to make an interface called person? And I'm gonna say no. So why is it asking about interface instead of a class? So because of this thing, we noticed if you immediately create a class, you kind of lose the opportunity to create an interface. And the uncle Bob's interface segregation principle is no client should be forced to depend on methods it doesn't use, so I start thinking about a person, does this object, does this greeter really need to depend on person? I feel like my person in my system is gonna do other stuff as well, it's gonna have more to it than just a name. So if I depend directly on person then person might end up with more API. And actually my class doesn't depend on all these full methods, my class just wants to know about a name. So it makes more sense for me to have an interface called something like named, you can have your own naming convention, name interface. If you have to. I'd rather depend on an interface, that question makes me sort of think I'd rather depend on an interface. So I'm gonna choose it's named. Oh, come on, come on. I don't actually know how to use PhpSpec at all. So it's saying do you want me to make a named interface? And this time I'm going to say yes. You're calling getName and the named interface doesn't have that method in it, do you want to add that to that interface, yeah. And now it fails, so if we look at what it's created, I've now got this interface for named that I've defined by thinking about how the greeter's gonna greet people, I've kind of generated an interface and later I'm gonna have to make some new implement syntax. And it's failing now with the red because I expected hello Ciaran but I got hello. Because I said when you greet someone whose getName returns Ciaran you get Hello Ciaran. But actually it clearly doesn't do that. So now I'll write some code. So I've got to accept a named thing. And then just run the test. It's good to get used to running the tests all the time. Oh, doesn't like it, oh because sometimes we call it without a parameter. So I kind of have to make that optional. I can do this thing now, can't I? Oh my God. Nope, oh no you can't. That doesn't let you not specify, it lets you pass a null. So if you want to make it optional, you still have to pass. So it's still failing because it doesn't say Hello Ciaran so now I have to do, I have to make it pass. So if you're failing tests, you want to make the test pass as quickly as you can. With the minimum kind of mental effort. Doesn't mean the smallest like code golf style amazing solution, means just like whatever you think of, do it that way. So I'm gonna do return, if, if named return hello dot named, can't type, can't type in front of people. So that passes. So this is the point where you refactor and make your solution better, so it's good to get it passing. It's easy to make it better when it works. Like if your car's broken, you can't start tuning it, you have to fix it and then start tuning it, so, there's probably something I can do better here. Well what I could do, okay, what I could do is have a thing called name, and in this case, it's empty. And then I do this thing. So now what? Oh, expected hello but I got hello with a space on the end. So now I trim that. That' works, still doesn't look good. Any suggestions? Oh I could turn that into a turnery couldn't I? It is name, that or that, so I can get rid of all of this and all of this. No, I did it wrong, what did I do wrong? Named, see how the test is kind of supporting me. That moment of panic I just had, I could see how to fix it, what other option we meant to hit undo, because it was passing like 10 seconds before, and then I thought I think I can make this change and then it fails. I could have just hit undo and gone okay it's working again. I'll try that refactor again. That's why having passing tests is so important, because you always know it literally just worked, so that thing I tried didn't work, but I can go back. I get about 2 minutes and then I kind of time out and get check out dash dash. Okay so it's passing, can that get any better? Oh what? Do what with a what? Sprint F? I can inline this, can't I? I don't think php has done that correctly. So I hit Undo, it's all good, I've hit Undo and the test tells me I'm safe again so my heart rate goes down. So you can see what it did, oh yeah it's because it needs some brackets, right? Yeah, that's good enough, so now I'll go to the next test. So I'll raise above the jet brains about that. That all makes sense, right? Oh I know how I can do it without the trim. No, we can spend too long on that. So we've described how if you give it something with a name it will say hello to that name, otherwise it will just say hello. So now we can go through the whole cycle again quicker, and we're gonna now make a person, I'll do it faster. So what's the first thing I do to make a person? PhpSpec, I have to describe it, I'm gonna describe a person, I'm gonna get a specification, I shouldn't have described a person, I should have described PhpWarks here, person, so to meet that thing. I'm gonna run it, says there's no such thing as a person so I'm just gonna let the tool fix that. What is there about a person? It_is_a_named, so it should have the type named. It fails. Someone's actually working on getting PhpSpec to fix that for you but it doesn't yet because it turns out to be really complicated. So what do I need to do to pass that test? Implements named. So what's the next behaviour? You go through this cycle, so, I'll make that bigger, sorry. It_knows_its_name. It's name is gonna be Bob. Notice when I was stubbing it in the other test, I said willReturn to sort of say when you're asked this is the data you're going to return, will is describing how we're setting up our doubles, our stubs or our knocks, should is always checking something. So getName should return Bob, why is it gonna be Bob? This be, how is that class gonna know it's called Bob? I'm gonna say be constructed with Bob. So when the class is constructed with Bob and then I call getName it's gonna return Bob. I haven't written this class yet, we start here in the description, so you start by thinking about the naming of the class, thinking about what the methods are called, thinking about what parameters those methods take. Do you want to give it a constructor? Because I'm trying to use constructor, yes. Now I've added a constructor, it needs to be constructed in both places. Cool, now it's failing because it expected Bob but got null. That's because this is the class. So now I have to write the code that implements this complicated behaviour. So gonna get name. Return this name. Seems alright. All this rubbish. That passes, so now we've got a class that, the behaviour we've just described was it knows its name. It can be renamed, so let's say we've got someone called Bob and then we call renameTo, getName, shouldReturn 'Alice'. Do you want me to add renameTo, yes. It got Bob, it didn't work. So that's a pretty, this is pretty simple behaviour to implement, we just need to set the name. Passes again. Hope that's given you an idea of the workflow. I'm spending my time at start thinking about what's the next behaviour, what's the API, what's the method called, what are the parameters, what's the behaviour? To an extent, the tool generates some of that boiler plate stuff for me and then I think okay, how do I achieve that? So that's one type of collaboration, the type of collaboration where one object asks another object for something. It's called a query, you always have commands, sometimes we care that an object calls a method on another object. I care that the email gets sent, I care that the invoice is approved, but the outcome of the example is that some method gets called on another object. used a thing called mocks. It's when it greets Bob, hello Bob should be logged. How am I going to describe that? I'm not going to describe that in terms of some return value the outcome of the example is the logger gets called with a method, the log gets written to. So we'll show you how that works. We use things for mocks or spies. They're very similar to stubs, instead of willReturn and stuff like that we're using methods like shouldBeCalled. The outcome of the test is this method is called, or should have been called. So example for the greeter is it logs the greetings. Let's go do that with a logger. And when I greet, then what should happen is logger, log, I guess, shouldHaveBeenCalled. So we do a different collaborator object. I'm saying when this object greets, the logger's log method should have been called. So how did this object know about the logger? I can run this already and I'll get prompted, there's no such thing as a logger yet, do you want to make it? Logger interface doesn't have a log method yet, but you're talking about it here, do you want to create it? Yes. And then it fails because it never got called. How might my,how can the greeter know about the logger? Inject it in the constructor, sure. BeConstructedWith the logger. So now I'll say hang on, the greeter didn't have a constructor, do you want me to make a constructor? Yes. That fails because in all the other examples, we didn't give it a logger. The greeter now has this constructor. We touch, oh. It has this logger thing which isn't being provided in the other examples. So what I can do, outside of all the examples in this spec I can use the function let. Let this beConstructedWith a logger. And now I don't need to have that in each example, it's kind of used in each example. And kind of magically this instance of logger is the same as this instance of logger. Kind of magically. So now I have one failing test, we didn't log the message. And that's because there's this logger and we're not doing anything and somewhere in here we need to log it, so I can construct a variable message. I can initialise that field as a logger. And then here I guess I do this, logger log message. And you see that everything passes. If I comment that out. It contains now I want to log the message. So in between failing tests I'm writing quite small bits of code. This example doesn't have a lot of main logic, so I'm not having to do a lot of thinking in each step when it's a really complex problem you're having to do a little bit of thinking in each step. Because you're breaking down a problem across multiple steps you're doing small amounts of thinking. So what have we built? Now we ended up with these types, there's a greeter that depends on an interface called named and we've made an implementation of the named interface. We also defined a Logger interface. We're probably not going to make a concrete thing, it's probably going to be an adapter to some logging library that we're gonna decide later, monologue probably. We can also run PhpSpec and it would output all of the examples for each class. So because we'll see maybe from that, these things become a way of understanding the code, you can hopefully read this and understand pretty much what the person does. It's constructed with Bob and has a particular type. It's constructed with Bob, it should say it's name's Bob, if it's constructed with Bob and you rename it to Alice it should say it's name's Alice. That's pretty simple, we can kind of understand what the object does by reading the spec. So PhpSpec focuses on being descriptive, tries to make the really common boring annoying stuff easy and automated, it tries to get you to do this pattern where you're designing first through the specs and then you're writing code. Current, I think the last release was 3.2. What is new matchers for warnings, because some people simply decided to start omitting those deprecation warnings and people want to test that. Previously ignored warnings is the thing people want to test because everyone uses exceptions now. But we added some stuff for testing warnings, and some matchers for testing iterations, iterators. Should iterate as this, this, this stuff. Under course of development, we're gonna reach version 4 in June, we do a kind of annual release cycle where each summer we drop deprecated things and bump some of the memo versions so version 4 is only gonna support php 7. Version 3 is gonna live until 2018 and then probably die. But die just means we're not fixing it, we're not gonna delete the tag or anything. And some things we want to build. When I said that the person should implement named, I want PhpSpec to say do you want me to make it into an interface for you? But that refactoring when you drill into it has loads of edge cases so that's kind of under development. We want to make it easier to use PSR 4, the moment you kind of have to replicate your different namespaces and folders into the PhpSpec xaml we want to read that from Composer. And somebody's working on that. And I've got a branch where we handle fatal errors. Even in php 7 there are fatal errors that you can't catch. We've got a strategy for dealing with that that involves processes. And we want to roll that out because it will make things on php 5 easier, php 5 users will be able to catch errors. So I want to get that into version 3, so it's available for version 3 that will still support php 5. This is me, I work for Enveeka, I should say that because I adapted enough to come here. So if you want to know about Enveeka we do software development and consultancy and training, and I do a lot of training, so if you want training come and talk to me. I maintain PhpSpec and i really want more people to be involved in helping the project and do pool requests. I also co-organize BDD London meetup which is every two months in London, which is 20 pounds return on the train and only takes an hour each way. And we're doing a twice monthly meetup where we talk about BDD with people from other programming languages and stuff. The videos are available online, and I guess if you've got any questions I can answer them. Does anyone have any questions, yes? Yeah you can, so here you're describing construction. We don't actually instantiate the class until you do something like this, that prompts you to instantiate it, so you could, for instance here, it doesn't make any difference in this test but you can override how it's gonna be constructed, and then it's actually constructed when this method's called what you can't do is then here try and change how it's constructed, you'll get an error message. So we support thing like named static constructors. Yes, you can do like, it's hard to explain. Since, let's do an example, instead of beConstructedWith Bob, I can do beConstructedNamed Bob. beConstructedNamed Bob. And when I run it, it'll say do you want me to make a static method called named? Yes, boom. And it will, you'll be able to construct the person by calling this. And if I haven't already got a constructor, it will say do you want to make a private constructor as well? If you don't have an existing constructor. But not everyone uses named constructor so I didn't mention it. But yeah you can override how the object's constructed as long as it's before something that would need the object to exist. Anything else? That was quite a deep dive, any more easy questions? - [Audience Member] Are there many notable projects that have been used? - At Enveeka we use it on loads of projects. Silius uses it which is an ecommerce platform. At one point, Laravel decided to start bundling it. I don't think that was Taylor's idea. But they added it as a dev dependency and then I think it later got removed. But it's hard to tell from packages because there's loads of Laravel projects depending it and then completely not using it. So it's really hard to tell from the stats. Silius is probably the biggest open source project I can think of. Because they use PhpSpec and Behat. - [Audience Member] Do you have, like a like an example that maybe you've done recently? Like just see how we bail out of, how crazy they can get? - No open source, PhpSpec tests itself with PhpSpec. And Behat. I think Silias is a good reasonable example of some big ecommerce framework that's trying to use this stuff. Most of the time it's used for the core domain model, because it's classes and not infrastructure, but frameworks tend to not have a domain model, unless it's something like an ecommerce framework that you're gonna sort of instal, libraries tend not to have it. We've used it for creating stuff like magenta extensions. Where we needed a nice clean, not everyone makes extensions as a nice clean domain model, but, we wanted a core domain model that's completely tested and then a layer of stuff that adapts it into a magenta extension. I think some of those are open source under Enveeka organisation. Yes. Normally this is too low level to talk to businesses about. So you do try and capture the words and phrases they're using in the business to describe these things, but, start asking, like if you're making a car for someone, asking them what size the nuts and bolts should be is like there's a mismatch there, so normally when I'm working on software, we've had conversations with business experts and tried to write scenarios we can use Behat to test, and then with those failing scenarios, then you're using PhpSpec, so, iterate through until the scenario passes. But I think it's really aimed, it's not readable by nontechnical people, it's meant to be really readable to people who understand php. Yeah, yeah. Yeah, when I've done event storming and then taking it towards a test, I then turn that into Behat test. And then,event storms, the past events map well on to given, commands map well on to when, and the events it produces map well on to then. So you can write that out as and use it in Behat or you can use PHPUnit or something to test the command is the right thing. Any others? Yes. Yeah, so, autonomous vehicle sounds cool. So if you think about your application, you've got different layers, you've got your core domain model which is how it really works inside. And then you tend to have, maybe you don't have it but you should. I'm gonna do an octagon just to confuse people. Like an application layer, this is the services that your application exposes that things like controllers would use. It's good to have that separation if you don't. And then you have stuff like user interfaces and databases, like frameworks other people write or infrastructure other people write that you have to plug into. So like from your symphony app, your calling methods in the application layer. So in terms of where these tools are aimed, the PhpSpec really kind of aimed at this core domain model. When the objects inside the domain represent the concepts of the domain and how they interact, and they're just pure php objects. I'll draw that bigger because you might have lots of domain and a little bit of application. And then things like Behat they tend to address this application layer, because Behat scenarios are written from a user perspective so they're representing things a person has to be able to do with the system. Whether it's through the UI or by ringing you up or whatever these are the actions a user has to be able to accomplish through this system, so you want to have an API here that corresponds to the things people do. Like if people constantly approve invoices you want a method called improve invoice. So driving this layer that exposes a service that knows how to prove invoices with use cases from people from Behat means that gonna be nicely aligned, but in the middle you might need to have a concept of an invoice and a concept of what approval means and that's what you're driving with PhpSpec. These domain concepts. A lot of people don't separate the domain and the applications, that's okay. Just makes it harder to rejig your application because you already control using as objects. And this Api can be services you're exposing, it can be having a list of commands that it can accept, something like that. Any others? When does this time out? If you have more questions I can do this all night. Ask me in the pub? Thanks everyone, hope you enjoyed it.