Ideas for efficient BDD with SpecFlow through examples
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 110 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/51156 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | |
Genre |
NDC Oslo 2012101 / 110
1
2
3
5
7
9
11
12
15
19
20
23
24
27
28
29
31
32
33
35
36
37
38
39
41
43
46
47
51
52
56
59
60
61
62
63
65
67
70
71
74
75
77
79
80
81
83
87
91
92
93
94
95
96
97
98
100
103
106
108
110
00:00
WhiteboardSoftware developerExpert systemProcess (computing)Software testingBinary decision diagramObject (grammar)Default (computer science)Exponential functionSoftware testingWeb pageFrame problemCurveSoftware developerHydraulic jumpNatural numberArchaeological field survey2 (number)Object (grammar)Student's t-testDataflowCASE <Informatik>BitMereologySystem callTrailGoodness of fitPattern languageProjective planeExpected valueOpen sourceMultiplication signINTEGRALTowerTraffic reportingForcing (mathematics)Term (mathematics)Latent heatCore dumpRight angleAcoustic shadowPower (physics)Process (computing)Formal languagePotenz <Mathematik>QuicksortCuboidType theoryIntrusion detection systemDefault (computer science)Binary decision diagramNegative numberSoftware1 (number)Entire functionProper mapSubsetComputer animation
07:59
Exponential functionSoftware developerCodeDependent and independent variablesComplex (psychology)Declarative programmingDevice driverElement (mathematics)Strategy gameContext awarenessGroup actionWeb pageObject (grammar)Euclidean vectorIdentifiabilityLoginMobile appOrbitVisual systemSynchronizationPasswordElectronic visual displayHome pageSimultaneous localization and mappingProduct (business)Equals signIntegerWeb pageRevision controlPressurePoint (geometry)Data managementComplex (psychology)Cartesian coordinate systemMathematicsObject (grammar)Software testingField (computer science)Computer clusterLevel (video gaming)Single-precision floating-point formatStatement (computer science)EstimatorCellular automatonPattern languageSummierbarkeitPerfect groupStrategy gameUsabilityReal numberNatural numberProgrammschleifeDevice driverWater vaporContext awarenessSystem callSocial classSoftware bugIdentifiabilityMultiplication signMultiplicationStreaming mediaSeries (mathematics)Sampling (statistics)Observational studyTouchscreenSoftware developerComputer configurationFocus (optics)Right angleTerm (mathematics)CodePopup-FensterLogical constantDivision (mathematics)Task (computing)BitFrame problemSoftware frameworkEndliche ModelltheorieElement (mathematics)2 (number)Installable File System
15:59
Equals signWeb pageVisual systemSample (statistics)Software developerEstimationDrum memoryMenu (computing)Planck constantOvalComputer fileObject (grammar)SummierbarkeitTouchscreenWindowArithmetic meanExecution unitCommon Language InfrastructureDecimalControl flowVacuumoutputTrigonometryoutputSoftware testingWeb pageCASE <Informatik>BitStrategy gameCartesian coordinate systemEstimatorField (computer science)Complex (psychology)DecimalPoint (geometry)Element (mathematics)String (computer science)Right angleLogicComputer clusterMaxima and minimaObject (grammar)Multiplication signVolume (thermodynamics)TouchscreenMathematicsGoodness of fitInstance (computer science)Social classDataflowEncapsulation (object-oriented programming)CuboidMultiplicationInjektivitätDecision theoryCodeImplementationQuery languagePattern languageKey (cryptography)Web 2.0Line (geometry)Extension (kinesiology)Moment (mathematics)Speech synthesisNumberBasis <Mathematik>Frame problemRing (mathematics)40 (number)Computer virusQuicksortRepresentation (politics)Metropolitan area networkCausalityLoop (music)Group actionProcess (computing)Endliche ModelltheorieBuildingUltimatum gameValue-added networkElectronic mailing listFocus (optics)Data structureEqualiser (mathematics)Green's functionCore dumpComputer animation
22:28
Mobile appSoftware developerElement (mathematics)Demo (music)AutomationGame controllerDevice driverPoint (geometry)CircleReplication (computing)Public key certificateElement (mathematics)Cartesian coordinate systemoutputSystem callWeb pagePattern languageObject (grammar)NumberLogicFamilyFood energyLine (geometry)Computer hardwareWordKey (cryptography)CASE <Informatik>Set (mathematics)Computer configurationArrow of timeHand fanComputer iconDrop (liquid)Strategy gameLevel (video gaming)Right angleVideo game consoleDirection (geometry)Multiplication signSoftware testingCuboidAreaDifferent (Kate Ryan album)Doubling the cubeCodeBounded variationPasswordTriangleDivision (mathematics)Computer animation
26:28
Physical systemOrbitMenu (computing)BuildingPasswordLoginDevice driverOvalString (computer science)StrutTouchscreenTablet computerDynamic random-access memoryComputer wormSynchronizationPersonal identification numberContext awarenessElectric generatorDemoscenePoint (geometry)Forcing (mathematics)Category of beingWeb pageSocial classInclined planeShared memoryTable (information)Cartesian coordinate systemHypermediaRight angleField (computer science)Software developerKey (cryptography)Different (Kate Ryan album)LogicRow (database)ImplementationObject (grammar)Speech synthesisView (database)Cellular automatonSystem callSoftware frameworkStrategy gameData miningChannel capacitySoftware testingAttribute grammarElement (mathematics)Game controllerGeneric programmingRepetitionHeegaard splittingCASE <Informatik>AlgorithmInformation technology consultingBookmark (World Wide Web)Flow separationOrder (biology)Physical systemWide area networkExtension (kinesiology)CodeCuboidPattern languageRule of inferenceElectronic mailing listWeightWrapper (data mining)Web 2.0OrbitMathematicsApplication service providerIntrusion detection systemText editorBitLoginComputer animation
32:29
PasswordOrbitDevice driverOvalSoftware developerGamma functionBuildingInclusion mapMenu (computing)Programmable read-only memoryWritingTouchscreenSpacetimePersonal identification numberSynchronizationSign (mathematics)Product (business)QuarkComputer fileGeneric programmingPhysical systemString (computer science)Order (biology)View (database)Newton's law of universal gravitationInterior (topology)Digital filterEmailPrice indexPlanck constantScalable Coherent InterfaceEquals signImplementationTable (information)Game controllerEntire functionProduct (business)BitObject (grammar)Cartesian coordinate systemWeb pageCodeCASE <Informatik>View (database)CuboidField (computer science)Different (Kate Ryan album)Power (physics)Element (mathematics)Strategy gameAreaOrder (biology)Category of beingData structureComplex (psychology)AbstractionProjective planeWeb 2.0Pattern languageData managementMultiplication signPhysical systemInterface (computing)Quantum stateExterior algebraTouchscreenFood energyInverter (logic gate)Staff (military)Uniform resource locatorLevel (video gaming)SpacetimeHome pageSystem call40 (number)MeasurementSpeech synthesisSoftware testingComputer animation
38:29
String (computer science)Software developerEmailElement (mathematics)Demo (music)AutomationDevice driverCodeSource codeCartesian coordinate systemGroup actionSummierbarkeitBinary decision diagramDefault (computer science)Object (grammar)Computer fileField (computer science)Independence (probability theory)FeedbackSpecial unitary groupDevice driverSet (mathematics)Software testingThomas BayesLevel (video gaming)LogicForcing (mathematics)Row (database)Compilation albumPattern languagePhysical lawObject (grammar)Web pageImplementationPersonal identification number (Denmark)Image registrationCartesian coordinate systemCASE <Informatik>View (database)OrbitNeuroinformatikSource codeProjective planeExterior algebraRight angleEvent horizonWeb 2.0Latent heatRevision controlPower (physics)Flow separationWater vaporRadical (chemistry)Software maintenanceElectric generatorInformation securityAlgebraic number fieldFunctional (mathematics)Cellular automatonScheidungszifferInsertion lossSoftware developerMultiplication signSocial classGoodness of fitDefault (computer science)Demo (music)Bit1 (number)Computer clusterEntire functionCodeField (computer science)Web-DesignerDifferent (Kate Ryan album)LoginComputer animation
44:25
BuildingOvalInstance (computer science)Rule of inferenceSoftware testingWeightBinary decision diagramGenderForcing (mathematics)Service (economics)HypermediaSoftware testingTable (information)Category of beingDivisorPresentation of a groupDecision tree learningGroup actionInformation overloadDifferent (Kate Ryan album)View (database)RootSubsetSpectrum (functional analysis)Pattern languageReflection (mathematics)Row (database)FeedbackLie groupAreaSoftware developerCASE <Informatik>Point (geometry)Multiplication signCore dumpImage registrationDot productInstance (computer science)Field (computer science)Cartesian coordinate systemRight angleRule of inferenceMathematicsKey (cryptography)Computer programmingProcess (computing)Electric generatorFactory (trading post)WebsiteOnline helpLatent heatSpecial unitary groupSet (mathematics)Repetition40 (number)Social classFlow separationSpeech synthesisCodeParameter (computer programming)NamespacePlastikkarteDefault (computer science)Binary decision diagramMaxima and minimaLengthDemo (music)Computer fileNumberDataflow1 (number)Natural numberComputer animation
Transcript: English(auto-generated)
00:00
Good. Welcome, everyone, to the first track session of NDC. I'm very happy that you are all here. I'm going to talk about IDs for efficient BDD. I have been doing BDD for a while, and I realized that there are some small patterns,
00:26
smaller or larger patterns that can make the entire development process much more efficient than without. I thought that this year I would like to talk a little bit about these patterns. There are a lot of patterns, and I had to select a few ones from them.
00:43
I hope that this will be still somehow valuable for you. That will be the session about. Let's wait just a few seconds more. Before I jump into the topic itself, let me very briefly introduce myself.
01:04
My name is Gaspar, and I live in Budapest. Don't try to pronounce my name. It will not work out unless you are Hungarian. I work at a company called Tech Talk. We are doing basically software development, and I'm working as a developer coach there.
01:22
I'm helping here and there. I'm also leading and creating an open source project, an open source BDD tool called SpecFlow, which has become pretty successful, at least compared to my expectations.
01:41
I had a lot of fun with that, and obviously all these things that I'm doing with BDD are somehow related to SpecFlow. Just briefly, I would like to also mention that we have another tool as well that we just launched commercially, which is SpecRun, which is for executing such integration tests.
02:04
If you have time, check out that one as well. Let's see the topic itself. If you have dealt with BDD and all these things around, then you know that there are a lot of terms that are related to this. BDD, acceptance test-driven development, specification by example,
02:24
and probably many other names how this thing is called. Different people are using different terms, and this is a little bit confusing also to me. Right now, how I feel today is that this is all about agile testing.
02:40
I think that maybe the core concept behind that is agile testing. What agile testing is, I don't know whether there is a proper definition of that, but for me, agile testing means to somehow involve the testing into the development process and really involve it, not just having as a shadow or some side step for development,
03:03
but somehow involve the testing and the testers fully to the development process. So that's what the BDD and all these types that I'm talking about are also fitting into. For me, in agile testing, there are three key concepts
03:23
or three main topics that are very interesting or very challenging. One is the automation itself, because agile testing is heavily depending on automation. The second is the idea of preventing boxes instead of fixing them. And the third is the specification or the testing language that you use
03:44
to be able to describe what you want to test. Today, because of the nature of this talk, we'll talk more about automation. So I could talk hours about the other two as well, but let's try to focus a little bit more on the automation aspect today.
04:03
For that, I'm not saying that I can make the session fully flexible, but it would be interesting to know how are you and what things you have used so far. So just please raise your hand if you are doing test automation. Okay, almost everyone, that's very good.
04:22
If you are using Selenium or Selenium WebDriver, a few of them, good. Are you also doing DDD or at least trying to do DDD? Uh-huh, good. Users of SpecFlow? Uh-huh, maybe other tools like Cucumber?
04:43
Okay, good. I hope that this will all fit for you. And as I mentioned, there are probably many ideas that I could talk about, and I had to select a few, one which fits into the frame of this session.
05:00
And finally, I've decided to talk about two things. One is the page object pattern, and not only the page object pattern, but the page object pattern and what is behind of that, which I think is pretty interesting. And the other is related to the default data, how you can fall back the testing to some default cases.
05:22
Let's jump into that. Let's start with the page object pattern. Probably you all know the page object pattern because it's a quite known concept. And why I still decided to talk about this was that I was talking to many people and I realized that people have slight misunderstandings
05:43
about the page object pattern, or they are not using all the powers that are in this pattern. So I thought that let's make a little bit deeper drive into how the page object pattern and the page objects work, and maybe that you can be inspired to do more for that.
06:02
So let's dig into that. Before I would jump into the code and show you how we do page object patterns and what ideas we have gathered so far, let's try to imagine what happens if you are not using page object patterns.
06:21
What pattern you are following if you don't follow the page object pattern. I don't know any good terms. I'm right now calling it brute force automation, which is more or less describing what you do if you don't use the page object pattern. It's not the best term because it has a little bit of negative impact. Brute force sounds like a stupid thing, but I think it's not stupid.
06:43
I've done quite many times brute force automation and there are some good times when this can be done. But still, I think in general and in a larger project, these have some problems or some issues where the page object pattern can help.
07:01
Let me quickly go through them. The first and probably the most visible thing is that if you do such a brute force automation, then the changing cost of the automation is getting into an exponential curve, which is quite well, especially if you are already in this deep part of the exponential,
07:23
in some cases, in some extreme cases, it can also happen that finally the test automation will be the bottleneck of your development, which is quite ridiculous because normally the test automation should be the one which is driving your development, not which is holding you back.
07:41
So that's why this is very dangerous. The other bad thing about this exponential thing is that in the beginning this is quite easy to do and you get a little bit of enthusiasm that, wow, this will work and so cool. We have already implemented the automation for two main features and then it gets extraordinary bad.
08:05
Okay, so this is bad, but why do we have this exponential cost? Let's try to dig into the reasons for that. And probably there are many reasons for this exponential changing cost, but one of them for sure is that we think, I'm pretty sure that almost everywhere
08:23
the test automation code is somehow treated as a second class citizen, even if you don't admit that. And this is more or less a fact and I think the more interesting thing is why this happens. Why do you think that the test automation code is somehow degraded compared to the normal code?
08:51
And again, probably there are many reasons for that. Time pressure, budget pressure, management pressure, whatever else. But I think one key point where we can actually do something with is that
09:03
usually we underestimate the complexity of the automation code. People when they first do such automation things think that this is pretty simple. You don't even need ifs and loops maybe or just very limited and you just make statements. And so this must be pretty simple.
09:22
Well, I think it's not. So I've done quite much test automation work and I think it's almost the same complexity level as the normal code, but obviously different. So why don't we see where is the complexity sitting into the test automation code?
09:42
And probably there are again many reasons, but one thing that is always bugging me and one thing that I always run into when I'm dealing with this is that in the test automation code it's very hard to find the levels that you want to extract, the levels that you want to turn from imperative to declarative
10:02
so that you want to extract to a method, to a class, to a layer, whatever else. Let me show you through this example what I'm talking about. So what you see here is a very simple Selenium statement. Probably everyone understands. It's just taking the save button by ID and clicks on it, right? So there is a very simple statement.
10:22
Let's imagine that I want to extract something from this. What options do I have and what would be the best thing to extract? Obviously the first idea would be that, ah, okay, I have a string constant there, the ID of the button, maybe the ID of the button will change. Let's go and extract the ID of the button.
10:41
And then if you go further, then you think, okay, yeah, maybe the ID changes, but maybe the next version we don't want to identify it by the ID because it will not have an ID or we want to identify it by the label. So why not just extract the code, the lookup strategy which is finding this button?
11:03
Or you can go even further because what you see here, this code is actually searching for the save button in the entire page. But what happens if I just want to search this save button inside the div because it's a pop-up or whatever? So maybe we should also, finding it by ID, finding the lookup strategy itself is probably not even enough.
11:23
We also need the context where it should find the save button by the ID. And you can even go further. Okay, let's say that maybe the entire stuff should be extracted because maybe in the next version of the application we don't even have to click on the save button, just hover the mouse or something strange.
11:42
So maybe this entire clicking is something that we want to extract to some concept. It's not easy, even for such a small statement. And I think this is one of the reasons why this test automation code is harder to imagine or harder to deal with than we think.
12:03
And obviously there is a kind of natural denial from the developers that usually we developers don't want to introduce something complex, don't want to introduce a new layer because we want to have everything simple and stupid.
12:20
And therefore, unless we understand the real reasons, the real goals, the real benefits that we get from another layer like from the page object, we don't want to introduce yet another layer. And this is good. And what I would like to do in this session is to find some benefits, find some reasons why it makes sense to do such extractions.
12:46
I'm hearing quite often that we cannot use the page object pattern because we actually don't have a page. We are anyway having a single page application. We are doing everything in Ajax. Everything is asynchronous and so on.
13:02
We don't have the page concept in our application. I think when you are trying to apply the page object pattern, you have to think a little bit of a more general way. Maybe this page object pattern, the term itself is not the best because it somehow focuses you,
13:21
trying to point you to the page which is returned by an HTTP request. But in reality, you don't have to think about this. So you can basically apply the page object pattern in any user identifiable element or concept that is in your page.
13:42
And I will show you some concrete examples. But you also see some hints here. For example, the pop-up window can obviously be a page in the page object pattern. If you have a tab view, then one tab can be perfectly a page in the page object pattern. Even the frame around your application can be a page.
14:02
So you should think about the page as a more generic concept. So, let's stop talking and I would like to show a little bit of code after that. What I have chosen to show you first is a page object pattern
14:21
from the Selenium WebDriver framework. The Selenium WebDriver framework gives you some basic infrastructure for page objects. I'm going to show you this. I would like to show you this in a perfect MVC application. A perfect MVC application means that this is somehow new,
14:41
it doesn't have any special HTML or CSS tricks. Everything you see on the page is having the same name in the domain model. So basically, the idle application for automation. Obviously, there are no such applications in reality because the real applications are always starting to differ from these original goals.
15:03
So what I'm using here is a stupid sample application that we are using for demonstrating an MVC and just to start a new MVC application. So it's an MVC application baseline, basically. And it's very simple. It has a list. It lists some user stories. You can even go to the tasks of the user stories that's not so interesting.
15:23
And we have a pop-up edit screen where you can change the user story itself. This is also very simple. You can just change the values and then save. The only interesting thing that I would like to highlight here is that we just decided to have a calculated field because it can be interesting in some scenarios.
15:41
So we have a calculated field which is called impact, which is the multiplication of the estimated days and the priority. So actually, if I change any of these and save, then this impact is also updated. It's pretty simple, but it will be fine for our demonstration. I have some scenarios that are describing this feature
16:07
and that are testing this feature. But I don't want to go through all of them. I just picked one which is a good representation, maybe. And this is about this calculated field.
16:21
So the scenario says that the story impact is updated when the estimation changes. And it looks like this, that given I have a user story volume with some details and this edit screen of this user story is open, and when I change the estimation to five, then this impact value is also changed. So I hope it's pretty simple and straightforward.
16:43
And what I would like to focus right now on is this Venn step. So let's try to look into the automation of the Venn step. If you know spec flow and if you know how generally these things work, then you know that for this step
17:00
I have a step definition method which is doing the automation itself. And right now this automation is using the page object which I have defined just here up. For defining this page object using this WebDriver framework, I just have to create a simple class story edit page. I have listed all the fields that you see on the screen,
17:23
including the save button. And for all fields, I have also identified or also specified the lookup strategy. So all these impacts and estimated days and all these things can be found simply by ID. So the ID of the estimated days input element is called estimated days.
17:45
The only trick is for the save button, because even in this perfect application the save button doesn't have an ID. But it was good for me to demonstrate that in some cases you have to use some other way to identify your element. So in this case this is using some simple express query to find the save button.
18:07
Generally the page object is not only good for listing all the sub-elements inside the page together with their lookup strategy, but you can also combine or also encapsulate some more complex automation logic.
18:22
Even in this application I have a little bit complex automation logic for the saving. And therefore I have created a save method here, which is actually just clicking the save button, and then it does something which you usually do when you first do some automation. You realize that this is somehow asynchronous and it's not so easy.
18:42
Let's put a sleeve there and we will fix this later on. I will not fix this right now and I need to fix this later on, so maybe it makes sense to make a comment here to find a better way. But the point is that I have encapsulated this complexity right now here,
19:01
so I have at least not in one place, so whenever I get the time to fix this asynchronous issue, then I can fix it in one place, and it's fixed everywhere wherever I'm using this. So after having this page class, there are some minimal plumbing code for this to work, but that's not so interesting right now.
19:21
I can just use it in my specflow scenarios. The easiest way to use such a page object is just to list it in the constructor, because specflow has a dependency resolving algorithm, it has a mini dependency injection framework, so whenever you are listing something in the dependency in the constructor,
19:41
then it will just create a new instance, you can use it in multiple step definitions and so on, and it will create the instance of the page for you. And after that, our method is quite simple, it just takes the storyEditPage.estimatedDays and sets the value.
20:01
Well, we see that setting the value to a text box is not so easy, but let's forget about this for a moment. And then we are just calling the save method, which is doing this encapsulated complex automation logic for saving. So this is a basic way of using the page object pattern, and if you look at the other ways to implement step definitions with that,
20:25
you see that they are all quite clear. One more step definition I would like to show you is this dance step, which is just doing an assertion on the impact. And this is also pretty simple, you see I'm just taking the value from the impact,
20:41
actually this is a span, and I'm just doing an assertion for that. However, one thing that I would like to highlight here is that right now I'm asserting the value based on the decimal value. So I've converted the expected value I have as a decimal, specFlow is somehow converting that to me,
21:01
and I also convert the value in the UI to a decimal, and I'm comparing it as decimals. This is quite a bias, probably, if you do that, but you have to see that this is a testing decision that I have made. I've made the decision that I don't care how it is written out, how it is formatted there, how many leading zeros and whatever I have there,
21:21
I'm just taking care that the decimal value, the numeric value, is the same. It could be another testing strategy to compare it with the exact string that you expect there. So since this is a testing aspect, I rather kept it in the step definition and didn't put it into the page object pattern.
21:40
Okay, so this is it, basically. What we have seen yet is that we have these two steps. One is the clearing the text box, another is the sending the new keys. If you don't like this, which I don't, then probably you can quickly come up with some extension method which is just saying set text,
22:00
set text and use it instead of these two lines. This is a very simple extension method. It just takes a web element and calls this clear and send keys. The only disadvantage of this is that right now I will have the send texts
22:23
for every web element, even for a div. But let's forget it for a moment and let's switch back to what we have discussed. At this point, what I would like to highlight is that even though this was a quite simple example,
22:40
we've got already some hints for the purpose of the page object pattern. And if you have listened, then you could identify two main values or two main goals for the page object pattern. One is that it was helping us to encapsulate any automation logic. Can it be the lookup strategy? Can it be the complex save button calculations?
23:01
Somehow I encapsulated that and reduced some repeating, so in normal code I would have an application for that. And the second is, which I was mentioning with this dance step and the assertion, it helped us to separate the testing concern, whether I want to test it by string literal or a number, and the automation concern, how do I get the value from this text box and so on.
23:25
So these are the two main goals, the two main purposes that the page object pattern can give us. And we will come back to this. However, just continuing from the last point that you have seen, we had this set text problem.
23:42
And first you would think that Selenium WebDriver is a stupid API because it doesn't have a set text, just the clear and the send keys. But believe me that the guys who have created the WebDriver are quite smart. And they on purpose didn't go to this direction to put such methods into the API like set text.
24:06
Because they have realized that they can work with pure HTML elements, they can do it very well, but then everything else is up to your application. This sounds quite strange for the first time, but the point is that actually our users, the users of your application,
24:22
don't think in HTML elements, they don't know what they are. They're just thinking in UI concepts. So if they see, for example, something which is looking like a box and they can enter text on it, they think that this is a UI concept which whatever they are calling, maybe they are calling it text box, but they don't know whether this is an input text
24:43
or whether this is an input password or whether this is a text area. For them, this is the same concept. It can be mapped to different HTML elements, but they are only caring with what they see and how they are using that. Similarly, if they see a text box and a small triangle next to it,
25:02
they think that this is something where I can choose from a list. They don't think about whether this is an HTML drop-down or this is the tellery control which is just rendering out the text box and the div for the choose options. They don't care about this. They are only caring about the goals they have that they can select a value from a list from that.
25:23
It happens that if you want to make somehow efficient automation, then you have to try to find these UI concepts that your application has and your users are aware of and try to automate those UI concepts because this is the way to provide the right encapsulation,
25:41
the right level of granularity for the automation infrastructure. The good news is that usually if you work with the application, it's quite easy to identify these UI concepts. The bad news is that this might vary application by application. So maybe in one application I have a funny way to select a value by a drop-down.
26:03
In another application I have a double text box with the left and right arrow button. And in a third application I have a third concept probably for the same. So probably there are some common concepts that are existing in every application, but in many cases this is slightly different, at least, application by application.
26:23
Let me show you an example for that. And this example finally will be a real application. This is a quite old application we have created five, six years ago. And it was working quite well, but recently we got some change requests and we have decided to put up some test coverage for that
26:44
to protect the existing features that we don't alter them with the new change requests. So we can say that this is a legacy application. This was done originally with .NET 1.1. And I have to thank the customer itself to allow us to show this in this demonstration.
27:03
I would like to show you how we did the page objects for this application. First I would like to start with the most simple page that every application has, the login page. The login page looks like, at least a little bit old school style, but it looks like this. It has a username, password, stay logged in, and a login button.
27:22
Pretty simple. Let's see how this looks in the code. What you can see here is pretty similar to the page objects that we have defined in the previous example, but there are two main differences. Again, I have listed all the username, password, logins.
27:41
I have the save logic there, I have the do login there. So from this point of view it's very similar. There are two key differences. One difference is that here I haven't defined my elements with the ivab element, although it's also automated with Selenium, but I have something like iTextBox, iButton, iCheckBox.
28:03
This sounds to be like a common API for something that I can share, but actually these are one of the UI concepts that we have in our application. Maybe the text box and the button is very similar in all of the applications, but how the button is, for example, implemented in your application might be completely different.
28:21
Maybe you are using a link, maybe you are using an HTML button, whatever. There are many ways to do a button. But now I have somehow encapsulated the user aspect, the user point of view with this. Actually, the implementation of this iTextBox is a small wrapper around the web element,
28:42
not too complicated, I can show you. Right now it's even having only this similar thing like the set text. Here it's called enter text, which is basically doing the same as we have done before, just to close the clear and the send keys. But I have encapsulated it somewhere in a nice place, and the infrastructure we have for these page objects
29:03
is somehow taking care of wrapping the web element into the text box implementation. It's not too complicated. The same with the button and the check box. So this is one key difference. Here we can define this UI concept, and the framework itself somehow wraps the elements into the nice stuff.
29:22
I will show you some more concepts later. The other key difference is that I didn't use any attributes to mark the lookup strategy for these elements. Unfortunately, in this case, this is not because they are so nicely defined that they can be always found by ID, but actually rather the opposite.
29:41
So we have realized that in this application, the lookup strategy for each element is so complicated that if you would like to describe it for each property one by one, then we would work a lot. There would be so huge attributes on each method, which we didn't want it to do.
30:01
However, this is an old ASP.NET application. So even the ID, you know, this CTRL00 underscore underscore underscore, and very complicated IDs they have. However, what we have realized is that maybe this application was done by .NET 1.1, but we have done it somehow in a way
30:25
that we were already thinking about some repeating UI concepts. One of the UI concepts that we have used is what you basically see here, is that all of these dialogue editors are implemented
30:41
with a little bit funny way of table layout. So all of these controls are in the table, and where all of the fields are in a row, and each row has one cell with the property name and one cell with the property value. Ah, and this is quite consistently used in the application. So this was giving us the idea that, okay,
31:01
maybe this pattern that we have used could be a good way to find our controls, to provide a generic lookup strategy which we can use and easily without providing many, many customizations and many, many attributes to define the lookup strategy for each. And even more. So we have realized that in this application we are using four or five different lookup strategies,
31:24
one for this edit text, maybe one for the buttons, and there are some more. And in our page object pattern infrastructure, really just a mini infrastructure, we have built up a rule set,
31:41
actually a prioritized list of rules, how you can find controls. And we did it in a kind of extensible way, and I can show you how this looks like. So this is the initialization code of this mini framework itself. The framework itself is not so interesting, but the ID is more than one.
32:00
So here we have listed all the lookup strategies that we are using in this application. And for example, one of these lookup strategies is this orbit by row. This is quite special in this application, but quite consistently used. And this is exactly doing what you have seen before. It just tries to find the TD with the property name, and then it tries to find the TD next to this property
32:22
and try to find the control inside that. So we have defined this strategy, and we have some other more. And after that, if you are just defining a property like this, text box and user name, the system somehow automatically finds the text box for you
32:41
based on this strategy. Because it's a priority list, it always finds the right strategy to use for that. And this is giving us a lot of power. This is giving us the power, for example, that I can do an automation for the page without really digging into how it was implemented and how the HTML looks like.
33:01
I just have to know that, okay, what do I see on the screen? I see user name. Then I can create a field called user name because based on the strategy, it will find it for sure. I don't have to overcomplicate all the automation code for that. And maybe for one page this is not so interesting, but if you have ten pages or even more,
33:20
then this is a lot of value and this is a lot of power that you have. Not mentioning the fact that you can afterwards even extend this with a new lookup strategy if you discover a new area of your application. So this lookup strategy is where the other key difference is what you can get as an extra power for a page object pattern.
33:44
And I would like to show you two more pages or page objects not so much in detail. One is the product detail page. I have to log in for that. And it's not a very complicated page.
34:02
Manage product details. And the only thing that you have here is this tab view. At the time when we have created this application, somehow these tabs on the web were somehow looking funny and we have also implemented it this way. Anyhow, we have many places in the application where we have a tab view.
34:21
This is yet another UI concept that our application is using. However, this is far from being a simple element like a text box. It's a little bit more complex UI concept, but it doesn't matter from the concept of the page object. If you are able to make an automation abstraction for this tab view,
34:42
then what you need to do when you are defining the project details page is you just put up a new field with this tab view interface, which we have implemented, and then you can use it. You can think about what such a tab view can do.
35:01
It's very simple. It can just switch to a tab. So it's a very simple implementation, but still somehow encapsulated. And actually what happened behind, this is already a Telerik tab view, what you see here, but originally we had some other tab view. And we were able to have the automation with the old tab view and then move to the new one just by changing how the UI concept is implemented.
35:23
This was a great power for us. Similarly, in our application we are using quite many rod table, Telerik rod table control, which can do a lot of things, much more than an HTML table. So we have a rod table interface that is encapsulating this logic.
35:46
So this is something a little bit more complex UI concept automation. And I would like to show you one more, and this will be the last that I show in this application. Is this the new order page.
36:01
Again, the page itself is not so interesting. The interesting thing is that for some reason we have used this funny way of creating a sublist into the application. And through the entire application there are many places where we are using the same concept. And the concept is about to have a list, to have a table with some controls.
36:23
The controls are different in each case. But what is common is that we always have an add and remove button and these check boxes that you can select to be able to add and remove the controls. This is yet another UI concept that we are using in this application. Let's not go into details whether it's the best or not so much the best.
36:41
But this is a UI concept which makes sense to attack if you want to have a nice automation. And in this new order page, as you see, I have this field which is having this add remove table. That's how we are calling this UI concept in this application besides the other drop-downs and save buttons and so on.
37:04
However, this add remove table is a little bit more special. If I show you the implementation of this add remove table, what you can see is that the add remove table is somehow looking like a page object itself. It also has an add button and a remove button.
37:24
And actually, in this setup, the same infrastructure is handling this add and remove button that we are using for handling the add remove and the other buttons and the other controls in the page objects. And this is very important. The importance of this is that if you are thinking about page objects,
37:44
you should not only think about the page objects as the top-level infrastructure, but it can happen that the page objects are nesting other mini-page objects, or however you call them. So the entire concept of page objects is working very well also if you are having such nested scenarios.
38:04
And page objects can nest very well, sub-page objects and sub-sub-page objects and whatever you need. And if you have once built up an infrastructure to finding your controls, defining your UI concepts, it's very easy to build up a new structure
38:21
which is really fitting to what the users need and what you want to automate. And that's very important. So that was about the automation of UI concepts. And I would suggest to you that
38:44
whenever you do such an automation project, you should at least think about what kind of UI concept your application is using. And you should think about whether it makes sense to automate those or at least to encapsulate the automation aspects of that UI concept because altogether this will give you a great power.
39:01
I'm pretty sure about that. We were talking about the page objects and web UI and so on. And this is probably looking out to all the ones who are not using web development but .pf or who are using, I don't know, just backend development.
39:22
And if you remember once more what were the goals of the page object, these two goals, one was to separate the automation aspect or to avoid the duplication of the automation aspect and the other was the separation of the testing and the automation concern. And if you think about these goals, then you will realize
39:40
that actually these goals have nothing to do with UI. These goals can work very well with any other things that you need to automate. So actually there is a generalization of the page object pattern which is sometimes called driver pattern where you are basically creating not page classes but driver classes
40:05
for different sub-functionalities of your application. And in these driver classes you are somehow encapsulating the automation aspect of that. I will not have time to show you more examples, at least it seems, for that. But for example, if you look at specflow source code itself on GitHub,
40:23
we have quite many automated tests for specflow itself which is heavily using these drivers. Specflow itself, at least this test doesn't have any UI to automate. It just has some compilation logic and some other logic to automate. And we are using quite heavily the driver pattern there.
40:43
If you want to have some examples, then I recommend looking into that. Good. So I hope this was giving you some insight or inspiration what benefits or what further things you have in the page object pattern.
41:03
And let me talk a little bit about the other pattern that I have chosen which is the default data. I'm not even sure that this is a pattern, but let's talk about the default data and how does it make sense to put default data implementations,
41:22
how does it make sense to separate the default data implementations in your code. The problem itself is the following. You want to make a test, and the test has some purpose. In this case, I don't know, uploading a file. And obviously you don't want to describe all the details
41:40
which are necessary to run this test from an empty computer. So you don't want to describe how to install your application, how to register the first user, how to create the second one, how to, whatever, doing some things. But you want to use some kind of default behavior for the things that are not important for this test.
42:02
So this is what this entire default topic is about. And actually if you dig into this default stuff, you will realize that this is not so easy again. If it would be easy, then I wouldn't talk about this right now.
42:20
And why is it so easy? Because here you have conflicting things that you have to balance against each other. And what are the two things that you want to achieve? On one hand, you want to make your test somehow specific because the test has to be repeatable. You want to avoid misunderstandings and so on.
42:42
You want to have maybe an easy automation. So therefore you want to have your test as specific as possible that really describes everything which is needed for finishing this test. On the other hand, you want to have your test somehow focused because you want to have it maintainable. So if the social security number field has been removed because of legal reasons,
43:06
you don't want to change all your scenarios. For example, the upload file, which has nothing to do with social security number. So one of the reasons to maintain your test is to make this focused.
43:22
And this is also helping you to have them more readable. So it's more easy to verify whether this test really makes sense or not. So these are the conflicting goals that you have to balance against each other. And as I said, this is not too easy. And in fact, these defaults can be defined in very different levels.
43:41
I don't want to go through all these levels. You see some examples for that. So the global level is some base set of data. The entity set is some default records that you are implementing. The entity, which is what fields defaults you have. And maybe even the workflow that you, for example, want to skip the login
44:00
if it's advised that you need to be logged in for this scenario and so on. The example that I would like to show you is a little bit focusing on this entity level default. And I would like to show you this through a very small demo. And this demo will be also in this Orbit application that you have seen just before.
44:24
And you can imagine that I have a registration page. And we have some steps which are related to registration. I have to admit that this is not the real code that is used in the automation.
44:40
I have simplified it for better readability. But from the concept point of view, it's working fine. So let's consider this first step that we want to automate. Even a registered user, full with the following details. And then you are just listing some field where you pair some details that you are interested in. But actually, you don't want to list all the fields.
45:02
You just want to list the fields that are important in this scenario. Everything else, you want to fall back to a default. I'm not saying that this is the most complicated development work that you can ever imagine. But to implement that, you can use a very helpful helper method.
45:20
This create instance, which is in the specflow-assist namespace. Create instance is a method which can take a table. This is a table argument which we have here. Take a table and convert it to an entity. So it just sets the properties by reflection. So it's a very simple thing. However, there is an overload of this create instance where you can also specify a file delegate.
45:46
And this delegate is used to create the initial entity before the fields are actually set. And what I have done here, I have created a method called createDefaultCustomer. Which is just creating the customer instance and sets the properties with some meaningful defaults.
46:05
And what will happen afterwards is that this create instance will call my method. It creates this customer with the default values. Then it overwrites with the values from the table. And finally, I have a customer which is using the specific values I have used.
46:22
And everything else is falling back to some default. Not a big magic, but this is a good way of separation. And actually there is another overload or another variant of this create instance which is called createSet. This createSet is not creating a single instance from the entire table.
46:40
But it creates a customer instance from each individual row. Otherwise it works the same. The only difference is that here the columns are the field names and the rows are the records. And again, this createSet also has an overload with the same delegates. So I can use the createDefaultCustomer method also for this other method.
47:05
And with this I can quickly create, in this case, three customers where besides the customer name and the invoice email, everything else falls back to the default. So this is a very simple example.
47:22
And I'm not saying that this is extraordinary without, for example, the createSet and the createInstance method. But what is very important here is that I have a single method which encapsulates all the defaults that I'm using in this application. So I could probably come up with some meaningful default for this test, for this step.
47:45
And I could come up with some other defaults for this other one. But I didn't do that. I have encapsulated the defaults into a single method which I'm using everywhere in the application whenever I need a customer. And this is very important because on one hand, sometimes you need to check what is the default value
48:04
because it might be necessary for debugging or for understanding what happens in the test. Or you can change the default because some rule changes apply. Maybe the max lengths of the V80 number or a minimum length has been changed and so on.
48:22
You have a single place where you can change all these defaults for a concrete entity. So this is also a small trick or small pattern but it can give you a quick benefit on a long time that you have somehow extracted the default generation for this customer instance.
48:44
So that was the small demo that I wanted to show you for the entity-level default values. Just a small wrap-up for these defaults.
49:05
It's one thing how you are extracting this default creation into some nice method or methods or classes or whatever you want but it's still not easy to find the right default. So this is always a big discussion also in our teams. But what are the good defaults?
49:21
How do we make sure that everyone knows these defaults or whether it's interesting to know the defaults or not? And here are some basic suggestions or some basic rules that we have discovered during our development process. One is that somehow these defaults should be kind of common sense.
49:42
I don't like this common sense stuff because common sense might be different for me and for you but still probably there is a small subset where we are all agreeing like the John Doe, I don't know, where this can be applied. The other important thing is that this should be well known for the team.
50:00
If the defaults are not well known for the team then if another developer, a tester, comes and wants to describe a test they will not rely on your defaults. He or she will rather write it out explicitly because he or she is afraid that your defaults will somehow alter the situation for that.
50:22
Obviously, the default has to be chosen in a way that it should be the least impact on the behavior of the application as possible. It's not always possible, so if you have to create a customer it has to be either a male or a female. If you have any code which is depending on the gender of the customer
50:41
or the user, then you have to pick one so there is no natural way for this. But still, you should try to find something which has the least impact on it. And you don't have to force too much default. So if you are unsure whether these defaults are fitting, rather write it out.
51:02
I think it's not hurting too much. One good way of finding out what are the good defaults is if you ask your teammate to explain the scenario. If you are human and they don't know that you are watching how he is explaining it then actually he will automatically discard the unimportant details
51:21
and he will explain it by mentioning the important key factors. And probably these are the ones that you have to mention explicitly in your test and everything else probably can be a default. So basically, that's it for the default data pattern.
51:46
These are the two patterns that I wanted to go more into the details. So what remains to me is to, first of all, thank you for your attention. And you see my contact details on the left-hand side
52:03
and I can also, if you search for my name, you will also find the spec flow and the other groups where I'm available. And additionally, I would like to recommend you three books which I think they are quite good in this area.
52:21
None of these are directly describing such efficiency patterns but they are giving some good hints how to do efficient BDD. Right now we are prepared, together with the program with Biklung, we are preparing a new workshop which will be first run in August. I don't have yet the concrete date
52:41
but it will be a two-day workshop going more deep into these details and with many exercises. So if you are interested in such a workshop, just drop me a mail or check the website of Biklung in a couple of days and we will have all the details there.
53:02
Do you have any questions maybe? I would be happy to answer them. Okay, I hope that this was not too deep in such an early morning.
53:21
I will be here after the session and through the entire conference. So if you have any further questions or anything that you want to discuss with me, just come to me and I'm happy to discuss it. Once again, thank you for your attention. Have fun for the rest of the conference.
53:41
And please, if you go out, don't forget to use these voting cards, session feedback. Like last year, there is red, yellow and blue cards. And if you write small comments on it, then it's also very helpful for me because I'm always interested in improving these presentations.
54:04
So thank you very much.