4.3 Acceptance Tests
For our last test type, we'll see how we can write acceptance tests using the Behat framework. We'll also get to see behaviour-driven development in action.
1.Introduction3 lessons, 05:32
2.Testing Methodologies3 lessons, 24:26
3.PHP Testing Frameworks3 lessons, 08:25
4.Test Types3 lessons, 36:05
5.Conclusion1 lesson, 00:36
4.3 Acceptance Tests
So far, we've learned about unit and functional tests. In this lesson, we're going to review our final test type, acceptance tests and we'll be writing them using the Behat testing framework. Now, as promised back in the Behavior Driven Development lesson, we'll also get to see the BDD style programming in action now as well. So, what are acceptance tests? Acceptance tests are basically the same as functional tests and that we can test an entire application's behavior all tied together. But they differ in that are human-readable tests appear as stories rather than looking like regular code. And we see an example of this in the BDD lesson. Now, let's actually put this into practice. Now, you should already have Behat installed. If you don't, check out the Behat lesson and return here when ready. So, we need to initialize Behat with our PHP application here. So, within your terminal or command line, make sure you're inside of the PHP Testing Basics folder. Let's run the following command. PHP vendor slash bin slash Behat hyphen hyphen e net. There we go, this creates a features folder to hold our Behat tests and bootstraps it to our application. Behat's now ready to use. Now, I'm just going to switch into sublime here. Now, just as a heads-up, you may have noticed that I deleted the test folder and the other associated files that we used in the functional test lesson. Just so that we can focus on acceptance testing specific files here. All right, let's begin. So, with behavior driven development, just like with test driven development, we're going to write tests before writing any application code. But we're going to do so by writing stories rather than normal looking tests. The benefit to writing tests in this way is that the tests are easily understood, even by non-programmers who might be involved in your applications development. So, I want to create a PHP function to determine if the current date is my birthday. Let's start by writing our first feature. Now, remember that a feature is something that we'll write to describe the behavior that we'd like to create within our application. Your feature should contain a benefit, a role, and the actual feature itself. So, now inside of this features folder, let's create a new file. I'll save this as birthday dot feature. Now, let's give our feature a name. I'm going to name mine, birthday. Next, we'll describe the benefit of our feature. Here the benefit is in order to see if it's my birthday. After the benefit, we'll describe the role of the user who might be using the feature. The user's role would just be a regular user. So, it would read as in order to see if it's my birthday as a regular user. Then finally we need to describe the feature itself. And there we go. So the feature is, I need to be able to call the is it my birthday method. And this completes the feature that we'd like to implement within our PHP application. Now, we need to write a scenario which will be used to test that the feature works as expected. You can write as many scenarios as you'd like to test your app in multiple different ways of using the desired feature. Your scenarios should contain a context, an event and an outcome. So, right after our birthday feature, let's create a scenario to test out our feature. We'll start with the scenario's description. Here I have set the description to say, determine if it is not my birthday. Now, we need to create our scenario's context. We do this by using the keyword, given, followed by the context. So, here's the first part of the context, saying Given I have a birth date of "May 12". You can see that the date is wrapped in quotes, meaning that this value will be available to us later on when we write our scenario steps. And I'll give you more information on steps in just a few moments. We can extend our context by using the And keyword. So, I’m going to extend the context to say, and today’s date is October third, wrapped in quotes. Next, let’s define the scenario’s event. This can be whatever should occur to trigger the scenario’s outcome. In our case, it’s going to me a method call. So, we can define our event using the when keyword. Our event is going to be when I call, is it my birthday? Now make sure to wrap the exact method name here, is it my birthday, in quotes, so that we have access to it in our steps. Now, lastly we need to define the scenarios outcome, which is the message that we expect to see, given the above context, when the is it my birthday method is called. We can define the outcome using the then keyword, our outcome will be, then it should display, no, today is not your birthday. Again this message here, wrapped in quotes, will be available to us in our steps. So, now we have our feature and a scenario to test the features behavior. Hopefully, you can see how these features and scenarios about a web applications behavior could very easily be written by a non programmer. They could then be handed off to a programmer to actually run the tests. That person doesn't even have to change the current format of the features and scenarios. They could just use them exactly the way they are to create the steps, and then execute the tests. Pretty cool stuff. Now, we need to tell Behat how to execute our scenario. When Behat encounters our story's context of given I have a birth date of May twelfth, it needs to know what to do here. We have to provide it with the code that it should run, given this context. We'll also need to do the same thing for the event and the outcome. To do this, we'll write steps for each part of our scenario. These steps are just methods, which contain the instructions for that step. Luckily, Behat simplifies this process for us by stubbing out some generic steps based on our scenario. We can do this by attempting to run our tests in the terminal. Let's switch back in to our terminal. And let's run the following command. PHP vendor slash bin slash Behat. There we go, this has attempted to run our tests, and it didn't work because we don't have any steps. Behat then recommends some generic steps that we can use, you canl see them here. So, let's copy these. Just going to start at the top, and select all of it down to the bottom. Let's copy it, and let's go back into our text editor, and under features bootstrap, let's open up the feature context dot php file. This is where we'll store our steps. Down here inside of the Feature Context class, let's just get rid of these comments, and let's paste in our stubbed steps. Now, I'm just going to scroll up to the top here, and let's go over what each step will do. Basically, the contact steps of this scenario, that's the first two methods here, will be setup methods to set up our tests and initialize the data that'll be needed for them. In this case, the data needed are the two dates of my birthday and the date that we're checking to see if it is my birthday. Now, we can access this data using the methods arg one parameter. As you can see, each one of our methods here has that arg one parameter. Now, we'll be renaming these to make them more descriptive in just a moment. We can then store the data needed into properties. So, we can use them later on in our other steps. Now the third method down here, I call, this is for our event. This method will be used to call our is it our birthday method, using data from the first two steps and then we'll store the result into an output variable. Basically, it's just going to execute the code that we're trying to test. Now, at the bottom, the last method it should display. This is going to be used to decide if the outcome meets our requirements. It will essentially evaluate if the output from our events method call is equal to the expected message passed into our step here. If not, we fail the test and display the actual output from our method call. We can then adjust our application to make it pass. So, let's begin filling in these steps. First, we need to make sure that we include in our application file that we're going to be testing, at the top of this file, before we forget to. So, right at the top, let's call the require method, and we'll require an A birthday dot PHP file. Now, let's create some private properties to hold the data that we'll need in our tests. We need to store my birth date and today's date that's being passed into the first two steps as arguments. So, down here inside of our feature context class, let's create a couple of properties. I'll create a private B date property. And another private T date property. We also need a property for the output of the event step. Where we'll call our is it my birthday method. So, let's create another private property named output. There we go. That's it for our properties. Now let's begin with our first step method to handle our first context. As you can see, the step name matches the context's name that we used and within the comment right above it, you'll notice a regular expression here which is used by Behat behind the scenes. Now, this is beyond the scope of this lesson. So please refer to the documentation for more information on working with these. Now, all we need to do within this step is to store the past end birthdate from our scenarios context. So, all we need to do here is store the past end birth date from our scenario's context into the B date property. We can access this using the arg one parameter, but I'm going to rename this to something more descriptive. So, I'm just going to change this to B date, and then inside of the method itself, let's assign the date value to our B date property. Next our second step is going to do the exact same thing, just for the T date property. So, let's update the parameter here to, say date. And inside of this, let's assign that date value to our T date property. Next, let's fill in the events step method. This method needs to call the is it my birthday method and pass in the stored B date and T date properties into it. Now remember, we have access to the method that we're calling for the test by using this arg one parameter. This is the actual method name from the scenario's event. So, let's rename this to B method. Now, let's call our method, inside of the step, we can use the method parameter and invoke the method using parenthesis just like a normal method call. Now, we're going to expect our method to accept two arguments. The first is the B date. And the second is the T date. There we go. So, this executes our method. Now, we need to store it's return value into our output property so that we can check the result in the outcome step. So, let's do that, we can use this, output. All right, let's now fill in our last step for the outcome. This method will simply use an if statement to check if the message that we expect to see from our method call matches our actual output in our output variable. We can access the message that we expect to see from our is it my birthday method call using this arg 1 parameter. Just like all of the other steps. Let's again rename this to something more descriptive. I'll change it to message and if it does not match that means our test failed and we'll throw an exception, showing the actual output from the test from our output property. There we go. So, here you can see I'm checking if the message does not equal the output, we're going to throw a new exception. This is going to say that the actual output is and then it prints out what the actual output was from the method call. That way we can see the test result and we can use that to correct our application code. So, that finishes our steps and that also completes our acceptance tests. Now, before we run these tests, I already know that it's going to throw a couple of errors because we've not yet created the birthday dot PHP file. Which is the file that we're testing, and we also haven't created the is it my birthday method. So, to save some time, I'm going to go ahead and create this file ahead of time. So, in the root of the PHP testing basics folder, let's create a new file. I'll save this as Birthday dot php. And inside of it, I'll open up my PHP tags, and I'll create my blank function. Is it my birthday? And remember this needs to accept the birth date and today's date. There we go. And we'll just leave it blank for now. Make sure to save all of your files. There we go. All right. Let's run our tests. And I'll switch back into the terminal and let's run the following command. PHP vendor slash bin slash Behat. All right. As you can see, our test do indeed fail but they did not fail the way that I intended them to fail. You can see here that I'm getting a notice saying undefined variable B date. Let's go back in to our feature context file, and you can see here that I'm referencing a B date and T date variables but I'm not using the right variables. These ones don't exist. We need to use this B date and this T date. Let's save the file, and let's try running our tests again. That looks better. As you can see, our tests are failing, but now they're failing in a proper way. You can see that we're just getting a failing test this time saying that, then it should display, no, today is not your birthday. But the actual output is nothing. So, by seeing this, we can use the results to drive the development of our application's feature. Let's go ahead and implement our application code to get this test passing. Let's go back into Sublime. And let's go into our birthday dot PHP file and let's give our function here an implementation. So, to make this function work properly, we need to make an If Statement to check if the birth date is equal to the today's passed end date. If it is, we return a string saying yes, today is my birthday. Else, we return another string saying no today is not my birthday. So, let's start with our if statement. We'll check if B date is equal to T date. If it is, let's just return yes today is your birthday. Else, will return, no, today is not your birthday. And, there we go. Our application code is complete. Let's go back into the terminal and let's rerun our tests, perfect. Our tests are now passing. And you can see that we get some good looking output here of the test results. You can see each step here that's ran. And you can see that all four have passed. So, feel free to add more scenarios to test our feature out further. Now, there is a lot more that you can do with all three of the frameworks that we've explored in this section of the course. I'd encourage you to take a look at their documentation to get more detailed information. Thanks for watching.