In this video, we'll talk about another advanced feature of Cucumber and Gherkin, and that is data tables, which allows us to set up quite elaborate data structures and easily bind them into domain classes that we've defined. So, what we'll do is we'll demonstrate on the presets that the microwave supports how we can create a whole bunch of presets and bind them directly into instances of the preset class using data tables. So, in this Microwave, we'd like to have some presets so that we can cook our popcorn or our frozen pizza slice or what have you. And, those presets are something that we're going to want to have set up for any scenario that we run, so that's going to be part of the system configuration. Now, what we could do is we could, at the beginning of each scenario, add in a set of presets. But, since the presets are sort of going to be the same across all the scenarios that we want to look at, that sounds like a lot of extra work. So what we'd like to have instead is a background section that says, "Before you run any of the scenarios, I'd like you to set up the system like this." So, we'll add that now. And because this involves quite a bit of typing, I'm actually just going to copy it from a different version of the feature file that already has this setup. So, when we write a background, what's going to happen is that before we run the body of any of the scenarios, it's going to run this background section. So, this is what allows us to set up a set of presets that are common across all test cases, and we can also set up the polling rate if we want to. This isn't strictly necessary. But the thing that we note here, is that we're starting to use these data tables because we'd like to define in a tabular form relatively concisely a bunch of information that's going to be used in the test. So, when you define a data table, the first row defines the names of the columns. So, this is the name of the preset. This is the time to cook that's associated with that preset, and this is the power level that's associated with that preset. So, you can see we have popcorn, and pizza slice, and cup of coffee, and cup of soup. We can make as many of these as we want. So, how is Cucumber going to deal with this Gherkin feature? Let's, as we usually do, just let it run and see what kind of dinner that we get. So, we'll do a "build" here. We'll save it and then it will say, "Sure enough. You have some step in your Cucumber that does not have any Java code associated with it." So, let's take a look at what we've got. So, what it has given us back is a function that takes a data table argument. And data tables are really cool, they're a very, very flexible data structure in the Cucumber language. So, let's add this in to our step definitions, and we'll have to bring in the data table, and we're going to do our usual trick of just printing out the structure of the data table. Rather than throwing a new PendingException, now the question is, how do we print out a data table? So, if we do just ... we can take a look at what Java prints out from this. So let's run this. That's right. I think there is, also I need to put in a polling rate function. So, let me go ahead and do that. So now, we can set the tick rate to "arg1", and the system will behave appropriately. So, that allows us to determine how fast our microwave pulls the external environment. All right. So now, we do a "build" and everything comes out okay, because we've defined all of the step definitions and we look at the console ... I should make this bigger ... and now it shows us in a nice tabular form what data we have. So, this is great. We can use this to initialize the presets that we have, and again Cucumber does some kind of amazing stuff. We can ask for that data table in a variety of formats. First, I'm going to call this "presets" just to give it a more descriptive name. Let's see what kind of things we can ask for. We can ask for the presets as several different kinds of lists. So, we can ask for the presets in terms of a list of complex data structures, and Cucumber will actually try and do the wiring for us. So, what it will do is it will look at the names that we have in the headers of the columns, and it will look for names of data items in the class that we're trying to build for this list, and then attempt to essentially fill in the values. So, we get a list of complex data and since we have a preset class for this Microwave, we're going to try and do that here in just a minute. But, we can also get back the data table as a List of List of Strings which is kind of the raw format of it. I think if you look at raw() here, it gives us back a List of List of Strings where each inner list is a row and the outer list is the set of rows or the list of rows. But, we can also ask for the data table as a Map. This only works if you have a two column data table, and if you have a two column data table what it does is it interprets the first column as a key name and the second column as a value, and so at that point, you can look up values given a key name. So, that only works for two column data tables. And finally, you can get the results back as a List of Maps. And in this case it uses the the row headers as the keys to a map, and it gives you back a map for each row in subsequent row in the table. So, that's a lot of information to throw at you at once. Let's actually try printing some of this stuff out. So, if we do ... Here we'll get a List of List of Strings. Let's take a look at what that looks like. So, here we can see an outer list with an inner list inside it. Let's try and do something here that I know will fail. asMap(). So, in this case, let's make our "keyType" a String, and our "valueType" a String, because that'll match in about anything. And, I think I have to do "String.class" here. All right. So, let's see what happens if we do this. And, what'll happen is we get a run-time error because if we look at the feature here, I said that if you want to return it as a Map then it treats the first column as the key and the second column as the value, but right now, we have a three column data table. So, there's no way for that shape to match the expectations of asMap(). So, if we look at the console, what we'll see is something that reflects that the shape is wrong. "The data table can only be converted to a Map when there are 2 columns." There you have it. So, we won't do that anymore but that gives you kind of a flavor for what's involved. Let's do it instead as, asMaps(). And, we can still do "String.class" and "String.class". So, what's going to happen here is that for each row, it's going to return us back and Map that associates the column header with the value. All right. So, let's try that out. And, it's done. So, let's look at it in the console. And here, you can see those Maps. So, we have a list where the first map associates "name" to "popcorn", "timeToCook" to "55", and "powerLevel" to "10". So, the thing about using this kind of List of Maps is that all the values, you could replace this with something more, obviously, that is closer into the type that you actually want. So, maybe for two of these columns here, we have "timeToCook" and "powerLevel", we'd really like those to be integers because that's how we plan to use them. But, the problem is that the first column in our data table is a string. And so, if we say that the values, the type of values is integer, it's going to fail to match on this first column. So, when you do a List of Maps, you have to be pretty coarse on the types that get returned, it's either usually String or Object unless all of the columns have the same type, in which case you can specialize further. But, let's get to the most interesting one. And, this one is in a way a mechanism by which you can bind the data that you have into some complex class that you've already defined. So, in this example, the columns that we've chosen, the column names, "name", "timeToCook", and "powerLevel", actually correspond exactly to the names of the fields that we've provided in preset. And when you do this, if you're able to exactly match the column names with variable names that you find in the class, then you can directly turn your data table into a list of these presets which is exactly what we want and doesn't require us to do any further conversion from the data table. So, how do we do that? So, we do this asList() method, and then we pass in "Preset.class". And now, it's going to try and bind the information that we defined into instances of this Preset. And so, we'll get one instance per line of the data table. So, let's take a look at that. Now one thing, I'm not sure if I've done ... I'm not sure if I've overridden toString(). So, the funny thing is that although this might be the format we want, it may make the least visual sense when we print it out, but let's give it a shot. So, looking at the console, now I guess the Java toString() method is smarter than I think. It's going to describe the type of the object and information about it. This almost seems too good to be true. I did define toString(). Okay, there it is. But, one interesting thing is that I'm actually missing one of the data members here. So, let me add that in, "plus", let's see. And then, this is going to be "powerLevel". And, we will define "powerLevel" here and then box it off here. All right. So now, we have something that prints out all the information that's associated with the class. Let's try this one more time. And looking at it in the console, there we go, we can see that we're binding appropriately all the names, the "timeToCook" and the "powerLevel" for our presets directly from this data table, which is kind of astonishing. It uses reflection and so thereby queries the class for the different names of the fields and tries to find them. But a clever user might think, well, what happens if I don't include a field or what happens if I get the name wrong? So, let's take a look at that case. So, I'm going to make a mistake here, "timesToCook", and now we have a mismatch. We have a field in the table; it doesn't match to a variable name in the class. So, what happens then? We get a failure. And when we look at the console, what we see is that there's no such field as "TimesToCook". So Cucumber, again, is very patient with us and clever about telling us where we've gone wrong and we can easily fix that. So, let's do that. Now, one other thing I'm going to try is doing partial information. So, suppose I didn't put in the "powerLevel", what's going to happen here? Let's do a "build". Now surprisingly, this passed even though we didn't completely fill in the data for the class. So, what does that look like? Well, let's take a look. Uh-oh! "powerLevel" is set to 0. So, if there's a missing field in a class, then it just gets set to a default value by Cucumber when it initializes the class. So, in my book, I'd much prefer an error here to a partial instantiation. But, this is how Cucumber does it, so if you're going to use data tables to directly instantiate classes, make sure that you have all the fields defined. So, now that we are going to add this back in, let's actually set up the presets, and then we'll call it quits for this lecture. So, if we go to the step definitions, what we can do here ... Oh, and one other thing you can do: so, rather than doing this operation on the data table here, you can actually set up the type of the argument list to be what you want. So, rather than doing this as list operation in here, we can do the following: "List<Preset> presets". And now, the Cucumber infrastructure is clever enough that it looks at the type that's expected by the Java function and it converts the data table into the appropriate type. So, let's modify our print statement here and just say "presets", okay? Give this a shot, and we can see that, that worked just fine. So, we can really start to make terse Java functions and a lot of the conversion is handled for you. So, let's finally do something useful with this. We've talked a lot about syntax, but we haven't actually set the presets which is why we want the function in the first place. So, all we're going to do here is ... Let's see, can I assign this directly. "microwave.presets = presets" So, I had it wrong. I'm a bad user, I think I have a global variable here. But, I can assign it directly from this List that I've constructed and then in later test cases, I can go back and use those presets. So, let's give this one more shot. Everything worked out and now we have a properly configured Microwave with presets.