Skip to content
CBT Nuggets
DemoBook a Demo

Intermediate Behavior-Driven Development Concepts

This skill delves into intermediate concepts of Behavior Driven Development (BDD), focusing on reducing repetition in test and feature files through parameterized tests and scenario outlines. It explores the use of PyTest BDD for linking feature files with test files, enabling more concise and reusable test scenarios. The skill also covers the use of background steps and keywords like 'and' and 'but' to enhance the readability and maintainability of BDD tests, providing a comprehensive understanding of creating efficient and effective BDD test suites.

Full skill from PCAT. Preview the training IT teams trust.

Updated

51m

Skill 23 of 31 in PCAT

What you'll learn

  • Write parameterized Gherkin steps with specific values, such as a user entering the number 10, to make BDD scenarios less ambiguous
  • Use a Scenario Outline with an Examples table so PyTest substitutes each set of values into a single scenario
  • Apply the Gherkin Background keyword to share a common Given step across every scenario in a feature file
  • Explain how a scenario-level Given step runs alongside the Background step when both are defined
  • Structure a user authentication feature with And and But steps so login scenarios reuse steps with minimal extra code
  • Write PyTest step functions with given, when, and then decorators that stay in sync with the feature file

Free 51-minute video lesson with a full transcript and knowledge checks. No account needed to watch.

Using Parameterized Steps

In situations where we want to test the same basic functionality but with different values, the current syntax that we're familiar with for test files can be extremely repetitive. That's where parameterized steps come in. In this first video, we take a look at the basics of parameterized steps and how they allow us to make our test files much more concise.

Knowledge Check

We must wrap the parameterized string that we're passing to @given, @when, or @then in the _________ function in order for the parameterization to work properly.

Using Scenario Outlines

Now that we've seen how to reduce the amount of repetition in our test file, let's see how we can do the same thing in our .feature files. To do this, we need to use something called scenario outlines. Here's what those look like.

Knowledge Check

Which of the following demonstrates the syntax for leaving a "space" in a .feature file scenario that can be filled in with different values?

Providing Background in BDD

Next, let's see how to reduce repetition even more by using something called "background" in our feature files. As you'll see, this allows us to consolidate repeated steps up at the top of our Features.

Knowledge Check

If we put a "given" step inside a feature's background, and then add a "given" step directly to a scenario in that feature, Pytest-bdd will use the scenario-specific "given" step and NOT the one in the background

Using "And" and "But" in Steps

Finally, let's take a look at a deceptively simple but important pair of keywords that can be used in .feature files to make our tests more reusable.

Knowledge Check

Which of the following could probably be broken up into multiple pieces using the "And" keyword?

Challenge & Solution: Writing Test Functions for "And" Steps

Now it's time for a challenge! In this challenge, you'll be asked to use what you've learned here to write test functions for an imaginary login page feature. Watch the video for more information.

And now that you've attempted the challenge, I'll show you how to solve it.

Knowledge Check

The @and decorator allows us to specify functions for steps that use the "and" keyword

View Transcript

Using Parameterized Steps

0:00Hi, Sean here and welcome to this skill where we're going to go a little bit more in depth with this behavior driven development stuff that we started on in the previous skill. So in the previous skill, we saw the basics of writing behavior driven tests, which as we saw tend to be more focused on the higher level functionality of an application versus the details of implementation, right? So while TDD tends to focus on like, you know, the little details, right, like the fact that a function that you define gives the right answer when you pass in certain arguments, behavior driven development, BDD tends to focus on things like what you see here, right?

0:42And these are from the previous skill. So if you didn't watch that, then I would recommend going back and taking a look at that before you watch this skill here. But anyway, the first thing that we're going to talk about here is something called parameterized tests and parameterized behavior driven tests or parameterized scenarios, I suppose you could call them as well, are essentially a way to run the same few tests with different values, right?

1:14So in our tests here, I purposely left out specific values from these, from the scenarios that we defined in our features, because that can tend to make your code very repetitive if you don't know how to, you know, use these parameterized tests that we're going to take a look at here. So again, just as a reminder, the way that this works, in case you, you know, need a little refresher from last time, the way that this feature file and the corresponding test file are sort of paired up in behavior driven development, at least when working with this, or is it here with this pi test BDD library that we learned about in the previous skill.

1:56The way that those are paired up is essentially in our test file, we just have to use these decorators in order to show pi test BDD. What functions are sort of the, the code manifestation, if you want to say it that way, the code manifestation of the lines that we have in our feature file. So if we take a look at this, some, well, here, let's, let's pick another one here. If we take a look at this, when they enter a valid integer number step in this scenario, where the user enters valid input, if we go over to our test file, we can see exactly what needs to be done or exactly what this actually looks like in code by taking a look at the function underneath the corresponding decorator for that, right?

2:46And pi test BDD actually does link those up behind the scenes as we saw in the challenge video, right? It actually looks for a matching step with this string in it, right? They enter a valid integer number. They enter a valid integer number. And that's how it actually orchestrates everything when we run our tests. So again, the idea of this is that the feature file serves as sort of a common ground between the developers and other non-technical folks so that they can speak the same language with regards to software behavior and linking them up with the actual tests that developers or testers, a lot of times those are the same people, linking those up with the tests that those people write makes it so that in theory, anyone should be able to run these tests and understand what's failing.

3:38Now, as we've seen previously, if we actually run these things, that output might not necessarily be the most, you know, manager readable thing or non-technical readable thing. But there are other ways that you can make it a little bit more readable as we 'll see a little bit later on. So anyway, let's get into the parameterized steps thing. First of all, let's say that instead of just kind of doing general steps here, right, saying the user is on the how many times, I guess that one's pretty specific there, but we'll say when they enter a valid integer number, you might want to be more specific about this and say something like when they enter the number 10.

4:24Okay. And then you could say then the program prints out and you'd actually have the exact string, right, or the exact numbers that the program prints out right here. Oops, let me just paste that there. All right. Now, this is a much more specific scenario that we've written here. And the advantage of this is that because it's more specific, kind of obvious, linguistically, I guess it's also less ambiguous.

4:54So, you know, when you when we had before, when they enter a valid integer number, and here I'm actually going to copy this here so that I can duplicate that like so. All right. And then I'll put it back the way it was and paste the old one down below. Oops, there we go. Just so that we have this around for reference here, okay, and we'll say instead of user enters valid input, we'll say user enters 10. Okay. All right.

5:24So with the old thing here, the user enters a valid integer number. It might not be super clear what that means, right? So in fact, when the user enters that number, it's actually a string that we're specifying, which is a little detail that could nevertheless have some important consequences down the line. So anyway, the point of this being that if we have specific values like this, it gives us something much more concrete to talk about when something ends up not working the way that we the way that we hoped it would.

5:58Okay. So the problem with this though is if we want to run a similar scenario with multiple different values, right? So maybe the user enters 10, but maybe we also want to run that with the user enters maybe two. Okay. And in this case, we would have when they enter the number two, and we would have the first two primes there. And maybe we also have another one where it's the user enters maybe something like zero, we might expect the program to output.

6:31Well, here, first of all, we would say when they enter the number zero, then we would say the program prints out maybe the string nothing. Okay. Something like that. Well, notice that there's a lot of repetition in here. And, you know, while certain things like this given step in each scenario, we 'll only have to write one corresponding function for that, right? We saw that just because of the way that pi test BDD matches things up, we're only going to need to define this thing once.

7:02But because we have different strings for each of these steps, even though they 're the same, what we would actually have to do is say, and here I'm going to just copy this here and paste it a few times so that we can use that for our example, we would actually have to say they enter or yeah, here they enter the number zero. All right, so I'm just going to say, we'll say, remove that there and say they enter the number zero, and then we'll say they enter the number two, and then they enter the number 10.

7:41So again, lots of repetition. The repetition is kind of spreading into the test file here. And we would of course have zero here, followed by two, followed by 10. And then what we would expect or what we would have to do rather is we would have to do that same thing for the then functions. So here's what that would look like. We would have to say here, I'll copy this one here, just like I did for the other one.

8:13Oops, let's try that again. There we go. All right, so there's that one, that one, and I'll paste it one more time there just to have the old one around. And now we would have to say the program prints. And then we would have in this case, 235, whatever. All right, we already have that there actually, but we need to replace that right here. All right, so we'd have to do that here. We'd have to do that for the two numbers. All right, so we'd say the program prints out two and three.

8:45And then we would have to say the program prints out nothing. And we would have to then go in here and modify the expectations. So we just leave that one the way it was. We'd have to change this one to two comma three. And we'd have to say that this one is going to be equal to the string nothing. So again, this is a big problem so far because we've just introduced a ton of repetition into our tests, not only in the feature file, but also in the test file.

9:18Now, as far as the feature file goes, there are ways that we can reduce this repetition further, but we're actually going to leave this for now because this would actually be something that would be nice to allow our non-technical coworkers to write out just so that they can be very specific about how the software should behave in different situations, right? So we can go back and show them how to reduce the repetition a little bit later . But the main thing that we're concerned with doing right now is we're concerned with reducing the massive amount of repetition in this test file now, right?

9:58So here's what we can do. Essentially, this works in a similar way to how functions work in Python, right ? But similar anyway, as you'll see, what we can do is because there's only like one or two little differences in these when, you know, in these when functions that we 've defined, as well as in these then functions that we've defined, what we can actually do is replace the parts that are the same with arguments. And actually this, this starts here in the decorator string.

10:30If there's a part of that string, right, a part of that step that we want to sort of leave open to different values, we can say they enter the number. And then in curly braces, we can say something like num, or we can call it n or whatever we want to call it. We'll just say something like user input. All right. And what that will do is that will tell a pi test BDD that we want this thing to match any string, right? Any step, well, any when step that is in our feature file that has that basic format with a value here, and we can basically just take that value now, and that will become an argument that we can use.

11:12So we'll just say user input like so. And then we're going to pass that in here by saying, here, we'll just use a template string there, user input. And that's going to be followed by that, that backslash n. Okay. So that's really all we need to do there. And we can remove these other ones. And what it'll actually do is that'll match all of the when steps there that start with they enter the number and then have some sort of value there.

11:42So if we run this thing, what we'll see, oh, and here before we do that, actually, there's something I almost forgot there, there is one more thing that we need to put in here when we're, you know, when we're using these parameterized steps, and that is we need to actually say parsers dot parse, and wrap that string like so. And this parsers thing is actually just something that we import from pi test B DD as well. So we'll say parsers there. And now if we run our code, what we'll see is that while the, not all the tests pass, right, because of this nothing thing, and we can just remove that one for now if we want to actually see them pass because they should.

12:22So I'll just remove that one there. All right. If we run this again, what we'll see is that sure enough, they all pass and it shows us that we're, you know, testing this for the values two and 10, right? Those have sort of magically appeared there. And we can do the same thing with the then steps, which we'll actually do in the next video. So I hope this has been informative for you, and I'd like to thank you for viewing.

Using Scenario Outlines

0:00All right, so now that we've seen the basics of parameterizing things in our test file, the next question that you might have is, is there a way that we can reduce the repetition inside the feature file? Right? In other words, if we know that we're just going to be kind of like reusing the same basic scenario, and by the way, let's put back that other scenario there. We'll come back to this in just a minute, right? If we know that we're going to be reusing essentially the same scenario with just a handful of, you know, sort of blank spaces here, swapped out, is there a way that we can allow the person writing the feature file to do this?

0:37Well, the answer to this is yes, and this is something called using scenario outlines. But before we take a look at that, let's actually go back to our primes program test file and do the same kind of parameterization that we did with the given steps, or sorry , the when steps that is with the then steps. So again, in each of these cases, we're just expressing some sort of expectation, right?

1:11You know, basically the output of the program. And that's really the only difference between each of these steps. So what we can actually do is we can parameterize these as well, just by saying something like expected output. All right, and we're going to have to use that parsers dot parse thing there as well in order to in order for that to work correctly. And now we can basically just replace this whole string with expected output.

1:43And that allows us to remove these other ones as well, right? This is going to essentially automatically pick up on the fact that there are multiple variations of this in our feature file, right, multiple variations of the program prints out blah, blah, blah, right? Yep, sure enough, we see that there's this one, there's this one, there's this one, there's other ones here too, which we might actually want to remove those just because now that this is parameterized, it's going to pick up on things like this, right, a message of disgust, you know, we might want to actually, we might want to actually specify the message that we want here.

2:22So we could say the program prints out how dare you, for example, all right, just like we did in the test here and oops, we only used one exclamation point there. So let's actually remove that like so. And this will actually be picked up on as well, right by this parameterized string that we have here, so we can actually just remove this thing. And that will be tested by our parameterized then step up here. All right, and we'll probably want to rename this thing here, because we're not checking for valid input anymore, we'll just say something like check output.

2:59And that's all we need to do there. So let's try running this again. And what we should see here, if we run it, oops, expected output is not defined . That's because I forgot to add it to the arguments here, right? So we're just going to say expected output like so alongside run program. And if we run this again, we'll see that one of these things fails. Right, that is this thing that we saw in the feature file, the first end prime separated by commas. So let's actually just remove this thing now. We don't need that anymore. And we can remove the other one here, just that corresponding then step, because we now have all of that parameterized.

3:35So let's try that again, and we'll have to come back to that nothing thing as well. In fact, let's actually just modify our primes program so that it takes care of that. We're just going to say something like if n equals zero, then what we're going to do is we're just going to print out nothing like so, and that should make that test pass. All right, cool. So we see all of our tests are passing now and we have those parameterized. But going back to our original question here at the beginning of the video, how can we do that same kind of thing where we reduce the repetition in the feature file?

4:12Well, here's what this is going to look like. All we really need to identify here is the things that are, well, the things that are different in between the repetition. So there's this number here, which would be the input. So we'll just say, we'll call that something like user input. All right. And then for the output, what we're going to do is we're going to say something like program output. And I'm just writing this all as like a one string and lowercase. You can add those.

4:42You can add the underscores there as well. That's perfectly acceptable. In fact, why don't we do that? Because that's a little bit better. And anyway, this for this scenario, we're no longer going to say user enters two. We're actually going to say something like user enters valid input again, because what we're going to also do is delete all of the scenarios that where that's repeated. OK, so user enters zero, user enters 10, user enters invalid input. These are-- this one's actually a repetition as well.

5:13We can specify this as the-- they enter instead of the number 10, we would say something like, well, I guess we've kind of made this more difficult by using the number in there. Because this isn't really a number. But we could say the number a, right? And what that'll do is that will actually pass that through the same when clause. There we go. They enter the number. And that a will be passed through.

5:44And we should expect to see this how dare you message there. So we can even delete that one as well. Although I guess we probably wouldn't want to do that because in this case, we'd have to put that under the scenario, the user enters valid input. This is the user enters invalid input. So we'll just leave that the way it is. So we'll just say something like the user enters invalid input a. But we can remove these other things here under the first scenario.

6:16And here's what this looks like in the same indentation space, the same indentation underneath this first scenario. We're going to say examples. And here's what this looks like. We actually create this little table thing. All right. And oops, there we go. We actually create this little table thing with the pipe symbols. And we're going to say user input. All right. So we're going to say user input and use a pipe symbol to divide that from the program output.

6:54And now that we have that, what we can do, we're basically just going to create a table with the expected values inside of it. So for the first one, if we want to test if the user input is 0, well, then our expected output there is going to be nothing, the string nothing. And then what we're going to do is we're going to do-- if user input is 2, we'd expect for the program output to be 2 comma 3. And finally, if the user input is 10, this is going to be a rather lengthy thing.

7:26We might have to extend these a little bit. What we're going to do is we're going to go back to here-- oh, it looks like I got rid of it there. I'm going to need those primes back. So hang on just one minute. We'll come back to here. While I-- there we go. I'm just going to go back to here, copy this, and then we'll put it back the way it was. There we go. All right. So let's paste that in there now. That's what I wanted there. And that should be all we need. All right. We don't have to move the whole thing over, although that might make it more readable if we were going to add more lines.

7:59OK, so what PyTest is going to do now is it's actually going to take all of these examples that we've created and substitute them into this scenario for user input and program output. So let's just try running this thing. What we're going to see here is-- there we go. We see that it's created multiple test cases here for 0 and nothing. That one passes for 2 and 2, 3. That one passes for 10, and then the first 10 primes.

8:32That passes. And then we have this invalid input on the prompt blah, blah, blah. That one fails because the step definition isn't found for that. Oh, that's just because I changed this when clause there. Let's change that back to whatever it was before. They enter a valid-- where is it? They enter a letter instead of a number. That's the one we want. So I'm going to copy that, paste that back there. And by the way, it is important to notice that this actually did warn us that we didn't have a matching test case for this one scenario that we defined or this one when step anyway that we defined.

9:12So that basically protects you from creating extra things in this feature file that don't have a corresponding test in the test file. Make sure that they're kept in sync with one another. So let's save this. And we're going to go back and run this again. And we should see that all of our tests pass. I'm going to remove that little dash s flag there. And there we go. We see that-- this is a little bit more readable now. And it shows that all of our tests pass as well as the values that we passed in for each of those.

9:44So anyway, that is the basics of using scenario outlines to reduce the amount of repetition in a feature file. So I hope this has been informative for you. And I'd like to thank you for viewing.

Providing Background in BDD

0:00All right, well, you've probably noticed by now that one of the main themes of this skill so far has been reducing repetition in behavior driven development code, right? So we saw how we could You know really reduce repetition by simply using the same steps over and over again We'll talk about that in more detail shortly as well because there's one more thing I wanted to mention about that but There's one thing that I want to mention here specifically first about feature files and That is you may have noticed that there's still a bit of repetition here in the basic setup right now It's a pretty common occurrence in behavior driven development that the the given step in a number of scenarios is gonna be the same right if you're you know testing out features of some sort of page or some sort of You know specific setup in an application and Python You're probably gonna need to have that same given step in a number of different scenarios And that's just really unnecessary repetition, right?

1:05Some people like to have that thing repeated over and over again just so that they know but Ultimately, that's just a bad practice to have that repeated, you know five six seven eight times right or more and That's why the gherkin syntax gives us something called Background that we can use to apply the same step to Multiple scenarios. All right, so here's what this looks like what we're gonna do is inside our feature Right our first end primes calculator thing here up at the top before the scenarios we're gonna add this background property and Essentially what this allows us to do this gives us a place to group everything together that You know that that we should have in common between all of the different scenarios now Usually the given step is gonna be the only thing in here that's repeated You know Really with most scenarios you're going to have a different when and then step that's usually the point of the whole thing So what we're gonna do here is we're just gonna take this repetitive given the user is on the how many primes prompt We're gonna put it in the background and we only have to do that once right so again this kind of

2:20Provides like shared code if you will or a shared step for all of the scenarios So if we add another scenario down here, it's automatically going to have this given step And so we won't have to we won't have to specify that again Cool, so that is pretty much all I wanted to say about that Let's just try running this thing so that we can actually see it in action Alright, so we're just gonna run pi test primes program test Pi and what we should see is that sure enough all of the tests run just like they did before Except there's a little bit less repetition here now that might seem you know that might not seem like a whole lot of savings there because actually We had to write this extra line this extra background line So we ended up with the same number of lines in here But the the nice part is if we were to go and add more scenarios now That would really reduce the amount of code that we would need to well the amount of extra lines that we would need to add Okay, now you might wonder what happens if we decide to add a different given step here, right?

3:25So if we go in here and add like another scenario, we'll just do something silly here. We'll just say something like user enters an empty string that's actually not too silly that's a valid thing that we would want to test and What we'll do here is we'll say we don't have to say given the users on the how many primes prompt anymore But just just for the sake of demonstration. Let's do that with something a little bit different. All right, so we're gonna say given the user is a Human alright, there's a silly part there right and this will just allow us to you know This will just allow us to demonstrate a different step in here So we're gonna say given the user is human and then what we'll do is we'll just copy these and Say something like when they enter an empty string Okay Then what we'll say is then the program prints out I Think here. We'll even put that in quotes. I think you forgot something okay, and What we'll see here is if we go into our test file, we're gonna have to actually add these steps Of course, so I'm just gonna copy these we'll go into here. We'll add we'll just add these down here We we don't have to sort them as you know given then when just because well, it

4:43's it's gonna be easier this way So we're gonna say I'm just gonna sort of change that into the given decorator there So we'll say given the user is human all right and then underneath this what we 'll do is we'll say human user and Then what we'll do is we'll just say something like We'll just print out the user is human just so that we'll see that all right and Then we'll have our other Steps there so we'll say something like define or here. We're gonna need at when okay, and then we're gonna bring that up here and say they enter an empty string and Here, we'll just do the other decorator here for this too And then we'll say then and then we'll say the program prints out I think you forgot something and let's just define those here real quick We're not gonna make any assertions in there. That's not human user. We'll say empty string Input okay, we're just gonna say print Entering an empty string Okay, and I'm just putting these functions here so that we have something to look at here and then we'll say define forgot something and Then we'll just print out you forgot something

6:00Cool, so that's all we should need to do there Let's just try this really again This is just a demonstration to show you what happens when you define something new oh and oops that actually didn't work because Notice that this the program prints out thing that was picked up by this thing up here Okay, so we actually want to change that a little bit so we'll change this to say the program says alright just to make that a little bit different there and We're gonna we might want to change this a little bit too I don't remember what we said for the the enter the number thing, but we'll just say something like they You know what we'll say they don't Oops, let's try that again. They do not enter anything and hit enter All right, that should be a little bit better. So let's just bring these back over here. Okay, we're gonna Piece that into the new test things here So we're just gonna say there we go They do not enter anything and hit enter and then we'll say the program says I think you forgot something Alright There we go, and let's try this again. Hopefully that works a little bit better than what we had before and Sure enough what we see this is actually maybe not what you expected

7:16It adds this the user is human step, but notice that before this We have this on the prompt thing which is from the background So in other words when you add a given step when you already have this background thing here What that's essentially going to do is that's just going to run both of these right? It's gonna run the background and then it's going to run this Given thing here. So in other words if you Really want to use this background thing make sure that that step really does apply to all of the scenarios in your feature because otherwise You're just going to be you know, you're gonna be complicating things for yourself, right in this case If you really wanted a different, you know a different given step here You would just have to create another feature there and put that inside of there without the same background So anyway, that is the basics of using background in feature files So I hope this has been informative for you, and I'd like to thank you for viewing

Using "And" and "But" in Steps

0:00Okay, so now that we've taken a look at how to provide background and features, we're going to talk about one more thing that can be really helpful when writing feature files that are fairly complex. And these are two important keywords, if you want to think of them that way, called and and but. Now these are both used in very similar ways in feature files, but in order to demonstrate these, what I actually want to do is just kind of break this out into another, you know, another example so that we can reduce the clutter that we've kind of accumulated so far.

0:36So I'm just going to create a few new files, really just two new files, one feature file and one test file. So we're going to say new file. And we'll say something like and but dot feature. Okay, that's just what we're going to be doing here. In fact, actually, let's rename that more after like some sort of fictional feature here. Let's let's say that we're building an application and Python that allows users to log in, right? So we'll call this like the login page dot feature. And then what we'll do is we'll have the test for the login page, which is going to be login page dot or underscore rather test dot pie.

1:16Now as you can probably imagine, if we were going to do this in real life, you 'd probably have a login page dot pie file, but let's just pretend that that exists and, you know, it'll just make this example go a lot faster if we don't have to actually build that thing for ourselves. So what we're going to do in here is let's imagine, you know, let's imagine some scenarios really just let's let's imagine the scenario where the user logs in successfully. Well, here's what that might look like. First of all, we're going to write a feature. All right, so we'll just say feature.

1:49And what we'll do here is we'll say user authentication, something like that. And now that we've done that, let's think of a scenario. All right, so let's some probably for a background here, we can say, given the user is on the login page, okay, that's just going to apply to all the scenarios in this feature, as you can probably imagine. What we're going to do now is we're going to define a few scenarios.

2:20And the interesting thing here is that these scenarios are really a little bit too complex to describe just using a single step. So let's just take a look at the successful login scenario, right? So we'll say scenario successful login. All right. And what this might look like is we already know that the user is on the login page, but you know, how is the user going to actually successfully log in? Well, they're going to enter in their email or username to the email or username text input there.

2:56They're going to enter in the correct password. And then they're going to click this, you know, login button or whatever, whatever we end up writing on that button. And those are really three different things that the user is doing. Now, the way that you end up dividing this up is really up to you. But the way that I would suggest you do it is you actually specify these two things here as given steps. All right. And then you make clicking the login button, the action, this will just make it a lot easier as you'll see in the tests to reuse these things later on, right?

3:33If we want to test, for example, what happens when the user clicks the login button without entering anything into these text inputs, we're going to it's going to be really a nice thing that we've split this up because we won't have to write a whole new, you know, a whole new piece of functionality for that login button. So here's what this is going to look like. And I'm just, well, first of all, I guess let me show you what this would look like if you were to do it, what I would call the wrong way, right? We've set up this given thing here.

4:03What we'll do is we'll say when the user, or we can say they, when they enter a valid username, a valid password and click the login button, right? That's a pretty lengthy when thing there. And the problem with this again is that because of the way that these feature files work with PyTest BDD, we wouldn't be able to reuse any of this over in the test file.

4:34All right. So we're not going to want to do that. But here, let me just finish this test here. I'll say, then, and this is just the expectation here, we'll say, then they should be redirected to the dashboard page and should see a welcome message or something like that, right? Or maybe a welcome pop up. Okay. So again, we see the same problem here with this then step.

5:07This is really two things that we're testing, right? They're going to be redirected to the dashboard page and they should see a welcome pop up. Now, interestingly enough, what the Gherkin syntax allows us to do in order to actually split these things up and make them more reusable is it allows us to simply put these things on different lines. So here's what we're going to do here. We're going to say, you know, when they enter a valid username and they enter a valid password and we already wrote and there, all right, but it's you should be with a capital letter there and they click the login button, then they should be redirected to the dashboard page and they should see a welcome pop up.

5:59All right. So again, this is just a sort of an add on step that Gherkin syntax allows us to use in order to make pieces of our application more reusable. So now if we go back into the login page test file, which we haven't written anything in yet and add separate steps or separate functions for each of those things, right? In other words, we're going to have separate functions for this thing here when they enter a valid username and this one here when they enter a valid password and this one here when they and they click the login button and that will allow us to rearrange those and reuse those as needed.

6:38Okay, so let's go back to login page test. Here's what this is going to look like. We're going to start off by importing from pi test BDD. We're going to import given when and then and here's the thing. There's not really a separate decorator for and, right? There's basically you just use the decorator for whatever step these are under. So we would put these under when since they're actions, you could also have them as sort of preconditions, right?

7:12Under given. And I think that's what I said at the beginning, but actually I think it makes a little bit more sense to have these as actions because again, we might want to test what happens when the user enters a valid username and there might be something specific that we want to check after that like that the box is outlined in green, something like that . Okay. So anyway, now that we've imported those things, we're going to define the basic steps for this and that is actually something I'm going to leave up to you as a challenge.

7:42Okay. So I will see you in the next video and I'll describe what exactly is expected from you and give you a few more details about the and keyword and how it relates to functions inside of here. So I hope this has been informative for you and I'd like to thank you for viewing.

Challenge & Solution: Writing Test Functions for "And" Steps

0:00All right, so as promised, here is the challenge video where basically you're going to take what we've written in this feature file and write test functions for it. Basically all you have to know, and I already mentioned this, but I'll say it again, all you have to know is that when you use and underneath one of these steps, such as when or then, you're just going to use the same decorator for it as you would have if, well, you're going to use the decorator for whatever step it's under.

0:31So you're going to use when decorators for when they enter a valid password, when they click the login button, things like that, right? Because these specify actions. And likewise, you're going to use given for any, we don't have any ands there, I suppose. You could say the user is on the login page, you know, and if you wanted to add some other precondition, maybe it's possible for the user to be on the login page when they're already authenticated. Well, sites don't do that, but you get the idea, we might say and the user is not authenticated.

1:04Okay, and you could do that there. Now, as far as writing these test functions goes, all you really have to do is print them out just to prove that everything's going on as it should in here. So one last thing that I wanted to mention too before you go into your challenge and actually write these things and run it is I mentioned that there was another keyword called but right inside the feature files that we could use. And really that does the exact same thing as and it's just more, it just makes more grammatical sense sometimes to do that or it really highlights, you know, when something is not normal, right?

1:46So you could say something like given the user is on the login page, but the user is authenticated, right? In that case, what we might expect, right, if the user is already authenticated is that the user will be redirected to another page or something like that, right? So again, this but does the same thing as and, right, programmatically, but grammatically, it really helps us to communicate our intentions a little bit more clearly.

2:17Okay, so I'm going to remove this one there. But anyway, that is your challenge is just to write the corresponding functions for these so that they run. And if you want to test out how much more reusable this is, try writing maybe another feature for or not another feature, but another scenario for an unsuccessful login, right, where the user enters a username and an invalid password and clicks the login button. And you'll see how much easier it makes, you know, how much easier these keywords make it to reconfigure things with a minimum of extra code.

2:53So anyway, that is your challenge. Feel free to give this about five to 10 minutes to complete. And once you've given it a try, you can move on to the next video where I'll walk you through the solution. So I hope this has been informative for you and I'd like to thank you for viewing.

Challenge & Solution: Writing Test Functions for "And" Steps

0:00All right, well, hopefully you've given this a try. Let's take a look at the solution. So I'm going to start off here just by copying some of these to make it easier to set up over in the test file. And I'm just going to put the corresponding decorators with each of these first. So again, this is going to be given. All right, so we'll say at given, and then we'll say the user is on the login page. And then for scenario, we don't really need anything for that. That's just something that's defined in the feature file.

0:30We're going to change this one now to when. So we'll say, oops, let's try that again. There we go, at when. And then we'll say they enter a valid username. And then we'll do and. But this one, of course, is going to turn into when, because it's under the when step. It's an action, right? And then we'll do the same thing for click the login button. So we'll say at-- there we go, at when they click the login button. And finally, we're going to do then.

1:01So I'll put a space in between there, just to make it easier to keep track of those. So we'll say then they should be directed to the dashboard page. And for and, since this is under then, we're going to change that to then as well. All right, we're going to remove and there. And we'll just say then they should see a welcome pop up. And now we just need to write the corresponding functions for each of these. So we're just going to make this one something like user is-- here, we'll just say on login page for that one.

1:34And we'll just say print. And we'll say user is on the login page. All right, let's not put exclamation points after all of those. That's going to get a little bit tiring to read. But we're going to do the same thing for this one now. OK, so we'll say something like user enters some username. All right, some valid username we'll say. And then we'll do the same thing. Oops, I forgot the function there. That should be enter username. And by the way, you may have noticed here that these two things here anyway might make for good parameterized tests.

2:12Because for now, what we've said is they enter a valid username. But what you could say is you could actually say something like they enter and you could have value into the-- and then you could say something like input name, text input. And you could use that to test pretty much any text input in the app that you're testing. So that might really reduce the amount of test functions that you have to create.

2:43But we'll just leave it the way that we had it first. So we'll do the same thing with our password now. So we'll say enter password. And we'll say user enters some valid password. And then we'll do the same thing for the login button. We'll say user-- there we go-- clicks the login button. And then for the then things here, we'll just say something like redirected.

3:16Oops, I already have user clicks the login button. Let's change this to something like is-- or here we'll say check-- checking, rather-- if the user was redirected. OK. And then last but not least, here we'll say something like check if redirected. And for the welcome pop-up, we'll say check if welcome. And we'll say checking if the user sees a welcome pop-up.

3:48That's all we really need to do there. So let's give this thing a try. And again, notice that there's nothing in this test file that's actually telling PyTest BDD when all of these functions should be run. The entire specification for that is in this feature file, which, as we've already established in this skill and previous skills, is editable not just by developers. So if someone wanted to sort of rearrange these things and test another scenario, it would be very easy for them to do that.

4:22They wouldn't have to know anything about writing Python code in order to make that happen. They could just come in here and say, when they enter a valid username, or better yet, if we were to parameterize this thing, you could have a string with symbols in it, for example, as into the username input, something like that. And ultimately, that would make it much easier for non-technical people to write those tests. So anyway, let's just try running this thing. We're going to run this file. Since we created a new file, we're going to say PyTest, loginpagetest.py.

4:54We're going to run it with the dash v and dash s flag so we can see what's going on there. And oops, it looks like that didn't work there. That's because we need to actually import the scenarios from the loginpage.feature file. So we're going to say, here we're going to import scenarios as well. And then we'll say scenarios. And that's going to be loginpage.feature, right? We're manually linking that up with this feature file because there's nothing in the naming convention that actually shows what belongs to what. So this says that these tests are for that feature file.

5:28Cool. So let's try running this again. And sure enough, we see that everything gets printed out in the right order. You can verify this, but it says users on the login page. User enters some valid user names, some valid password. User clicks the login button. And then we're checking those two things down at the bottom there. So all that would be left, if you wanted to actually use this for real testing, is implementing the actual functionality for each of these things. So there's lots of libraries that you could use for doing this, if this were a real page.

5:58And that's something that we're going to take a look at in a later skill. So anyway, that is the solution to the challenge. I hope this has been informative for you, and I'd like to thank you for viewing . [BLANK_AUDIO]

Frequently asked questions

What does the Intermediate Behavior-Driven Development Concepts lesson cover?

The lesson covers parameterized Gherkin steps, Scenario Outlines with Examples tables, the Background keyword, and And and But steps in a user authentication feature. It also shows how to write PyTest step functions with given, when, and then decorators that stay in sync with the feature file.

How long is the Intermediate Behavior-Driven Development Concepts lesson?

The lesson runs about 51 minutes and includes a challenge the trainer suggests giving five to 10 minutes. It is one part of the Certified Associate Tester with Python (PCAT) course, which a Training Manager can plan at about 26 hours per learner before practice time.

How does a Gherkin Scenario Outline with an Examples table work in PyTest?

Instead of writing a separate scenario for each input, such as a user entering 10, 2, or 0, the team member writes one Scenario Outline and lists the values in an Examples table. PyTest then substitutes each set of values into that single scenario and reports the result for each one.

What happens when a BDD scenario has its own Given step and the feature file also has a Background?

Both steps run. In the lesson's demonstration, the shared Given step from the Background runs first, followed by the scenario-level Given step, so a scenario can add its own setup while still inheriting the common step.

Why should a team use parameterized Gherkin steps instead of general ones?

Specific values, such as stating that the user enters the number 10 and naming the exact output, make scenarios less ambiguous. The trainer notes this gives the team something concrete to discuss when a test does not behave as expected.

How does this lesson relate to the PCAT-31-01 exam?

The lesson belongs to the Certified Associate Tester with Python (PCAT) course, which is associated with the Certified Associate Tester with Python certification, exam PCAT-31-01. It supports the course's BDD coverage, helping QA engineers and Python developers on an IT Director's team apply behavior-driven development with PyTest.

Team training path

Turn this skill into assignable team training

This free skill is a preview of the courses your team can assign, track, and report on with CBT Nuggets.

What's next?

Ready to keep going?

For your team

Bring this training to your team

See how CBT Nuggets helps IT teams close skills gaps, hit compliance targets, and prove training ROI.

Book a Demo
Just need PCAT?

Learning on your own? Browse individual plans ($49/month, billed annually) →

Not ready to buy?
with no purchase required. Already have an account?
Book a Demo