Transcript
This transcript was autogenerated. To make changes, submit a PR.
Hello everybody, I am Catalin Tudose. Welcome to my talk.
First, a few things about myself. I am currently acting
as a Java and web technologies expert at Luxo
Mania and I am also an author at Pluralsight
and Manning. I have recently published the Junit
in Action third edition book. This talk is also
related to automated testing in Java. The name
of this talk is developing Java applications with cucumber.
You are going to find out how to use
cucumber, a behavior driven development framework,
in order to make your applications
better respond to the business needs. Otherwise said, you are
going to address the needs of the users and
you are going to do the right thing. First,
probably you are accustomed with working TDD
or test driven development. TDD is a well known
technique. It has been introduced many years ago and
it starts with a simple idea. First you are
writing a test in order to verify some
feature and as the feature is not yet implemented you
are expecting this test to fail. Then you are writing the
code to address that test. You would like to write the
shortest piece of code in order to make that test pass.
So first you write the test, then you write the code. You are
driven by the test and after that you make the
test pass. Eventually you refactor the code
so you improve its internal structure while keeping
its external behavior. And this is a simple
but efficient technique and it has been applied for
many years. However, it comes with some shortcomings.
First, tests are tightly coupled to the implementation,
so you have to understand and know some
details of the implementation in order to
write good tests. Tests are focused on the method or class
that they are testing and you have to have
a good knowledge about what they are doing. We can say that
you are doing the things right, following things test
driven development, you make sure that your code is
working fine, but at the same time the business goals
are neglected. In most cases, you don't follow
the business needs and you don't have a good understanding
of how the application must react to the user's
input and to the user's need. We come to the
next step, behavior driven development, which is a developing
technique that is based on BDD. It is
a methodology of developing it solutions that directly
satisfy business requirements. So you have the business requirements
in mind and you would like to directly satisfy them.
You would like to address what the user needs.
You keep on adding business value to the application
and this is your main focus. Behavior driven development
is a developing technique that also facilitates the communication
between the business analyst and the developer. So you have
a vehicle that will facilitate the communication and the
interaction between these key people inside the project.
And if TDD is making sure that you are
doing the things right, BDD is making sure that
you are doing the right thing, meaning that you directly satisfy
the user's needs. Our vehicle for this demonstration
will be a flight management application. We are
presenting a company that is serving passengers and
is providing flights for these passengers. And this
company is following a few policies in
order to allow passengers to apply for their flights.
We have two types of flights, economy and business.
And we have two types of passengers, regular passengers
and vip passengers. And the company has defined
its own policy for adding and for
removing passengers to the flight. We are going
to present some scenario here about adding the passenger to
a flight. We say here that if the flight is economy, we are going
to approve the request so everyone can join the economy flight.
Otherwise, if it is not an economy flight and
this means it is a business flight, then we have to look at the
fact that the passenger is a vip or not and we
can allow the passenger to join a flight
only if it is a vip. If the flight is business,
otherwise we are going to reject the request. So if
we have a regular passenger, he or she cannot join
the business flight and this is part of our business
logic. Of course, the company is maintaining
some policy for removing a passenger from a flight
and I am going to show how this looks like in
the code. We are starting to introduce
this idea of behavior driven development by showing
how the application looks like and showing how
some junit five tests are running. I will start by
running these junit five tests and introduce cucumber a little
later. Just to understand how you make the step between using
a general framework like Junit five and using a
specialized BDD framework like cucumber, let's have a quick
look together at these classes.
We have this abstract class flight
which is defined by the id and by the list of the passengers.
It is extended by the business flight.
And here we have the policies to BDD
and to remove a passenger.
As we are presenting. We can add a passenger to a business
flight only if the passenger is a vip. Otherwise,
we are going to reject that request and we
cannot remove that passenger from the business flight
once he or she has been added.
For the economy flight, we also have some
policies both for adding passenger and removing passenger.
If you want to add a passenger, okay,
he or she is accepted. If we want to remove
the passenger from the flight, we can
remove it only if he is not a vip.
If he's a vip, we are returning false. Meaning that if
the passenger has been added to an economy flight
and he or she is a vip, we cannot remove him
or her any longer. And the passenger is
defined by the name and
by the facts if he or she is a vip.
Let's have a look at what we are using
in our project here.
We're saying that first we are trying to use only
junit five. Pure junit five. And here I
have added the dependencies for junit five.
Junit Jupyter is one of the components of Junit
five. In fact, it is the API for writing tests.
And this is the test that I
have written here in
order to demonstrate how the policy for adding
and for removing a passenger to a
business flight or to an economy flight works.
And I have already used the facilities of junit
five. And I have written some nested tests in
order to group how they are working together.
You see here that we are having some economy flight and
Mike and James are passengers and
they belong to this nested class. That is
economy flight test. It belongs to a larger
class here. Airport test.
It means here that when we are executing
this airport test, we are going to execute the
nested tests. We see here
that we have airport test, having nested test, economy flight
test and another nested test,
business flight test.
And here I
also have a nested class regular passenger
inside economy flight test.
I can say here that we have two levels of nesting.
Airport test contains economy flight test, while economy
flight test contains regular passenger.
And here vip passenger.
So I
use here the facilities of junit five,
including here the display name annotation that
will make the execution of the test easier
to follow. For each test, we have here
a setup. Economy flight, which is the flight
and the two passengers also
for other tests, have here other
setup.
Let's have a closer look at what the tests are doing
before each we are initializing the
flight and the two passengers. For the regular
passenger, we check everything here
that is working correctly.
The id of the flight, the fact that the passenger has been added,
the size of the passenger set, the fact that Mike
is among the passengers. When we try to remove
Mike, which by the way is
a regular passenger, we succeed here.
And after removing the mic passenger,
the size of the passenger set is zero.
What I use here is assert all method.
Together with this assert equals.
What's the benefit of using this assert
all method that has been introduced by junit five?
If I was writing only assert equals after assert
equals after assert equals, then if some
execution was failing, let's say here at the third,
assert these assertions were not checked
any longer with this approach. If I write here
assert all, then I make sure
that all assertions are verified. Even if
we fail here at assert equals number three,
the next assertions will be executed
and I have here more testing.
What I have made sure is that I used the junit
five facilities, the display name,
and we are going to see together how they look like
and which is the benefit for using them in practice.
And this assert all method that is using multiple
asserts here.
Let's run this test just like any
other usual junit test.
Okay, what's the big
benefit here of using Junit five
and the facility of display name? You see that
it is possible to read these nested tests
just like natural language and
we can read here together. Given there is a premium flight,
when we have a vip passenger, then you can add and
remove him from a premium flight.
Given there is a premium flight, when we have a vip passenger,
then you cannot add him to
a premium flight more than once. And you can keep
on reading here also
here you can read given there is an economy flight,
when we have a vip passenger, then you can add him but
cannot remove him from an economy flight.
This is how BDD looks like
BDD starts with a concept of given
when. Then these are the keywords
that are used in the
BDD approach. Given something,
we have a starting condition.
When we do some action then we
are expecting some results.
And here you may have a
first taste of how BDD acts because
junit five is providing these nice facilities of nested
tests and of display name so
that you can follow easily how
this execution looks like.
Let's have here how such a passenger
policy may look like in terms of BDD
keywords. We implement here a feature called passengers policy
and we say that the company follows a policy of adding and
removing passengers depending on the passenger type and
on the flight type. For each feature I can implement
one or more scenarios. And the scenario from here is
the scenario with economy flight regular passenger.
If I follow the given when, then keywords,
I can read it in plain English. This scenario
given there is an economy flight when we have a regular
passenger, then you can add and remove him
from an economy flight. You cannot add a regular passenger to an
economy flight more than once. So we have a given
condition when represents some action we are
pushing on something on the side of the application that is
constructed and some consequences are
expected. Then something else. Things is some
core concept of behavior driven development. You have some precondition,
something that is given you do some action. When is the keyword
here and some consequences are expected.
Then coming back here to the code, I run again
this airport test just to emphasize
these ideas of behavior driven development.
And I see here again that I can read all
the scenarios in plain English with
the given when and then keywords given.
There is an economy flight. When we have a vip passenger,
then you can add him but cannot remove him from an economy flight.
Things is fine, but we can do better. It is
time to introduce cucumber as behavior driven
development framework. This is the first cucumber
project that we introduce. Now just to emphasize that
the structure of the classes here is absolutely the same
as previously presented, but we move
our attention on something else on the test.
I have just presented the way how
a feature is written and how a scenario is written.
And what I used there was the Gerkin
syntax. What is Gerkin? Gerkin is the language
that is used by the cucumber framework
in order to describe the features
and the scenarios. And all these scenarios
and the features are written here in a specialized
file called a feature file.
Everyone can read it in plain English. Junit the
way we were previously doing
I can immediately read it just the way I was
doing a little earlier. What's particular
about cucumber and what do I need in order
to start using it?
First I look here in the palm XML
dependencies and I see here that business
the previously introduced junit five dependencies.
I introduced these cucumber
dependencies. They will be needed
in order to execute the tests first,
to annotate the method and to execute them.
Okay, this is a feature file. I can read
it in plain English, but how can I transpose it
in code? If I have a
look here, there is a
passenger policy test that
is already written. Let's have
a closer look at how it looks like.
And you see here that there are plenty of methods that
are annotated with given when.
Then this is something new.
These are some new annotations. Where do they come
from? If I look here at the
import, of course they are coming from the cucumber
packages. And if I have a look
here, given there is an economy flight
and if I have a look here into
the feature file, I see that there is a correspondence between this step
from the scenario end of this feature
file to this step here.
So whenever we'll
execute this step from the scenario, in fact
things method will be executed. Then if
I have a look here, when we have a regular passenger,
there is a corresponding step here. The method
annotated with the when annotation and we
have a regular passenger and this
is what is going to be executed at
this step. Then you can add and remove him
from an economy flight.
Then you can add and remove him from an economy flight. It is
the corresponding step from here. This end step
here is in fact another consequence, another then
and it has been annotated
here. The method with this then
annotation as well and
what was previously written with nested test and
using only junit five facilities.
It is written here using cucumber and
the cucumber annotation. The cucumber annotations
we see here that there are no more
nested tests. Let's have a look back
to our previously
demonstrated junit five tests.
We see here that we are having airport test having economy
flight test included nested and
economy flight test has regular passenger and vip
passenger as nested tests.
While here we
are having a linear structure.
All methods, all test methods are at the
same level.
How can I execute such a test?
In order to do this,
I needed to add this cucumber test.
This is a special file. It may have any name.
Just I chose cucumber test for convenience
and what I need to say here, I need to
run this test with
the cucumber runner and I
provided here some cucumber options.
Pretty will mean the way the tests
will be displayed. We are going to see this really quickly
and where to look for the feature
files and we say here look on the class pass
and you are going to find out
this features folder more
exactly this one. And there
you are going to find the feature file
or the feature files and you are going to make some
correspondence between the steps that are defined
here and the steps that are defined in
the Java
code.
Okay, let me run this youtuber test.
Okay. What I see here is that I still have
some scenarios,
but it is easier to read
them of course in plain English.
Just the way we are doing previously with junit
five and the junit five capabilities.
Given there is an economy flight, when we have a regular passenger,
then you can add and remove him from an economy flight
and you cannot add a regular passenger to an economy
flight more than once.
Let's compare this with
the previously executed test
from junit five.
You see that they are looking pretty similar,
but the cucumber ones are
easier to read. You don't have to go into some depth.
They are linear here and you
can eventually immediately read the whole
scenarios here into the
feature file. If we want to do some
more comparison, let's look at the
length of the code here for the
cucumber version, the code
written for the test is about 157
lines, while here it
is 207 lines. So we also
have some gain here.
At the level of the code we have a shorter
code and the shorter code is easier to understand, to follow, to maintain.
Just because cucumber as
any behavior driven development framework in general, is providing
us some facilities to write linear test and to
write each step that is repeated to define
only once a method for it. What I do mean here
is here, if I have the given there is an economy
flight step and I have again given there is economy
flight step, there will be only one
method annotated with given there is an economy
flight here. So you have a
shortage of code. And of course things comes as an advantage.
What do I need in order to easily
use cucumber in my projects?
I was saying of course first of all, remember I need
to add these dependencies. Great.
What else, what else is needed or at least recommended
working with the cucumber plugin for
intellij here brings to some facilities. I'm going
to demonstrate them immediately. Just to say here
that I needed to add the cucumber for Java plugin
and the Gerkin plugin. Remember I was saying earlier
that Gerkin is the language that
defines the scenarios that cucumber
needs to use. Cucumber is the framework while
Gerkin is the definition language.
Let's see what this plugin can help me
to do if I want to
execute directly these scenarios. This feature with these
scenarios, would that be possible?
If I right click on this feature file,
let me execute it. It is possible as
you saw that Intellij
was providing a run button, I can directly execute
it and I can see here
I have six scenarios 22:06 steps,
22 past great.
What did I need to do in order to
be able to directly execute this
feature file? One thing was to have
this cucumber plugin installed. Then I
need here to have some cucumber
Java configuration that needs this main class as
executor cucumber API Cli main glue
is the BDD airport meaning the
package where I wrote the code. You see
here I have BDD airport as the package where the code is written
and the feature or folder path is where the feature
file or feature files are to be located.
After this pretty simple setup, as you
are seeing, I am able to directly execute
this feature file. And remember this
is very nice feature, especially for
nontechnical people or even for technical people if they
are maybe in a hurry,
you will immediately get feedback about the scenarios and steps
to be executed and about their results.
Okay, let's have a closer look at the
step from this feature
file to this Java file.
And you may ask yourself, how can I do
such a thing? Quickly, how can I
write this method and how can
I quickly take the annotation,
the annotation that is needed for such a step.
You see here we have this given there is an economy flight
and it has added, cucumber has added this given and this
argument there is an economy flight.
Let's have a look. Let's presume that this step
is missing here from the implementation. If I have a
look here at the feature file,
the plugin will immediately detect that,
oh, this step is missing and it will
highlight it with another color. And if
I try to execute now this feature file, just the
way I was doing a little earlier,
let's see how it goes.
Okay, it fails, not a surprise for us,
says here undefined step given there is an economy
flight,
and if I have a look here,
I am provided an advice. You can
implement missing steps with the snippets below and it
immediately gives me this given there is
an economy flight annotated method and I
can just copy, let's presume
that I didn't have it and paste it here.
It was similar to this already
implemented method and I can
just write the Java code that corresponds to
that action. So you can immediately obtain
the skeleton of the test by executing
directly this feature file and by taking
the not yet implemented steps from here.
Just to tell you that there is no semantic
difference between these steps given when,
then these annotations do
not have a semantic difference between them,
they just correspond to the steps from here.
And of course they have some understanding significance,
because if you write
given and you read given, you understand that this is a precondition.
If you write or read, when you understand that
this is an action, and if you write or read,
then you understand that this is a consequence.
Let's move forward and let's try to implement
some more functionality. You may ask yourself, can we
write more complicated tests? Can we write some tests that
need some input for their methods? Can we write
some parameterized tests? Yes, this is possible.
And in order to demonstrate this,
we take this feature about providing bonus points to
the passengers. We say that the company has
a bonus points policy saying we would like to calculate
the bonus points. What do we need to do? We take a look. If the
passenger is a vip, we take the mileage and divide
this mileage by ten and we are getting the bonus points.
Otherwise, if the passenger is not vip, we are taking this
mileage and we are dividing it with 20 and this
is the bonus points. So let's have a
look at the implementation of this new feature. It is
a parameterized feature. The scenarios for
these parameterized features are known in cucumber as scenario
outlines. So we have this separate feature saying
bonus policy. The company follows a bonus policy depending
on the passenger type and on the mileage.
I can immediately read here scenario outline
regular passenger bonus policy given we have a regular passenger
with a mileage when the regular passenger travels
mileage one and mileage two and mileage three,
then the bonus points of the regular passengers
should be points. You see that we have some
parameterized steps and at the bottom of this
scenario outline we have a table with examples
and for this table with examples we have mileage one,
mileage two, mileage three and points.
And you may immediately understand that there will be
a one to one correspondence between the values from
this column, mileage one that will be introduced
here, the values from this
column that will be inserted here, the values from these
columns to be inserted here to mileage three and the
values from this points column to be inserted here.
So we have four columns
corresponding to these four parameters and
we have five lines meaning that things scenario
outline will be executed five times for
each set of values.
And you see here two scenario outlines the scenario outline
for the regular passenger bonus policy and the scenario
outline for the vip passenger bonus
policy. Of course the same example
stable with one to one correspondence between the columns
and the parameters from here.
These scenarios were transposed here into
these methods. You see again
this one to one correspondence
between this annotated method given annotated method and
this step from here and
for example between this when annotated method
and this when step from
here and what you need to see
to observe in addition is that these three parameters from
here have been translated into these
three regular expressions from
here and into these three parameters of
the method.
So when the cucumber plugin generated
this step, it has generated this when
annotated method and
a method with three
parameters, mileage one, mileage two and mileage three,
each one corresponding to one of the regular expression
d plus here means some integral value.
Again this step from here, then the
bonus points of the regular passenger should be points has one parameter.
It has been translated into this then annotated method
and this regular expression
from the argument and the method with one argument here
int points just
to have a look here that a
new mileage class was introduced in order
to make the calculus for the
given points, for the points to be awarded to the
passenger according to the mileage. This is pure business
logic here.
And following the same strategy,
when you write this test,
first you think, what do I need in order to
provide the business logic for this given step, which are
the preconditions? What do we mean that we have a regular passenger
with some mileage? What does it mean that
the regular passenger travels some mileages?
What does it mean that the bonus points of the
regular passenger should be something?
And this cucumber
test is the same one.
This cucumber test is the same one. I can right
click on it and execute
it. Let's wait a little here
we are interested about this bonus policy new
feature. We see here scenario outline with examples.
And we have the five lines of
examples that were present into the feature.
The same here we
have here five lines with examples.
This means that the test is executed
once for each line and it checks
accordingly for each input stat.
Let's try to execute things bonus policy feature directly,
just the way I was doing previously with the passenger
policy feature.
And we see that we get immediate feedback.
We may read here ten scenarios. Remember we are having
two scenarios outlines, each one having five
examples. So two multiplied by five means,
ten scenarios and 30 steps.
Let's have a look here at how the methods
are generated and presume that
this when method is not present here.
You see here that the
cucumber plugin will immediately emphasize
that the method is not implemented. Says undefined step reference.
And if I run again this bonus policy
feature,
okay, it is failing. But if
I have a look here, I am told,
hey, you are missing this step.
And the cucumber plugin
is providing immediately the when annotated
method with these regular expressions and
the method with three arguments.
So let's go back here to bonus policy reintroduce
things method. Have a new look here.
Okay. The plugin has found out
that the method is back and
if I want to re execute this chuchumber test,
I'm expecting everything to be working and green again.
Okay. And this is what is happening.
So we are getting
closer to our end of the demonstration.
I hope you have enjoyed this cucumber
facilities and that you are going to
find out that it is useful for your projects
and you are going to adopt this BDD style.
Of course, there are more features and more capabilities
of cucumber that are waiting to be discovered.
Our demonstration introduced the BDD concept
and the core cucumber facilities thanks
for watching. See you in the next presentation.