Web App Testing & Tools

Testing the Form Validation

Miško Hevery

Miško Hevery

Qwik Creator (Previously Angular)
Web App Testing & Tools

Check out a free preview of the full Web App Testing & Tools course

The "Testing the Form Validation" Lesson is part of the full, Web App Testing & Tools course featured in this preview video. Here's what you'd learn in this lesson:

Miško uses Playwright to test the form validation. The additional selectors are added to the page object. When the submit button is clicked, the tests verify the error has the correct text. The final code can be found on the lesson-7 branch


Transcript from the "Testing the Form Validation" Lesson

>> We now have a form, and this form validates. So we would probably like to write a bunch of code to validate that the form behaves correctly, so that it does cause invalidation if I produce different values, right? And so we can do that. So I think, I don't know if it will validate if I do it.

It was a minimum distance, no. I see, it only validates when I hit the button Cluster. Okay, got you. I don't remember what I wrote. Right, so it has to be bigger than that. Okay, so let's do that. So what we can do now is write more tests along this path, right?

So we can say, test(it should print validation errors when out of bound parameters are passed in). And we will, again, follow the same pattern. And so on a clusterPage, we basically wanna say clusterPage.set. Actually, let's navigate to a clean page, and then we can say, setDistance(1), clusterPage.submit, expect(clusterPage.error).

So this should be distanceError).toHaveText. Something like that, right? This is easy to read, right, from a point of view of what the test does. And so now, our job is to modify the clusterPage, the page object, to provide the code that is needed here, right? So let's create a setDistance.

So you do this.page, here we go. That was helping, input[name=distance]. Yeah, here we go. [name=distance], distance.toString. Yeah, that seems right. await, sorry, not expect, async. So distanceError, that will be return this.page.distanceError. No, I think it's a different one, what is it? Inspect > Elements. It is class error.distance, okay.

So .error.distance. No, this is not sitting, right? Let's type here. That's gonna give me that. And then the last thing is gonna be page.click, and it's cluster not button. So now, if we If we go back to our play right, we should be able to validate this. Looks like our click locator is not working, so notice how it's hanging.

And it's trying, basically retrying, and at some point, it's gonna give up and say, I waited long enough and it's no longer a thing. So let's see. The correct thing is Inspect, it's button type= submit. Okay, sorry. So it's not that, it is, button-
>> Missing async.
>> Did I leave the async out?

Yes, I did. I have a distanceError, and this is mistyped. Here we go. There we go, now it's passing. So you can see that we asserted in. And the nice thing is when you go up through this, you can actually see what it was clicking on, right? You can see that it highlights, this bit was filling this bit out, then it clicked on a button, and this showed up.

And so all of the pieces that you have are there. So the main strategy I wanna point out in terms of testing is this idea of page objects, right? And again, it comes down to this idea of, I wanna make sure my tests are a form of documentation, therefore, they need to be easy to read.

And I wanna have a test like this, right? This is a test that I can easily read, and people understand what's happening here. And so now, we need a place to kinda put all of the non-interesting bits. And the non-interesting bits basically go into your page object.
>> That also reads like something you could show to a non-technical person, a product manager or somebody like that, and say like, hey, this is the-

>> Yeah, so this is basically known as a DSL, right, domain specific language. And somebody was asking, what is the job of a QA? I think what would be useful, if your QA's technical enough, is to create these page objects, right, and essentially create this DSL so that you can create complex scenarios.

For this particular case, we only have one page object. But you can imagine how you click on a button to navigate to another place, and then what you get back is a page object for that navigation. So you are, for example, on a clusterPage, but you click on a settings, and what you get back is the page object for settings.

And now, you can do necessary things with the settings. You can go and change your preferences, save it, and then go back to yet another page, and then verify that the correct output is there. And so the DSL that you create with these page objects allows you to succinctly express the intent.

Whether it's in the form of navigations or in the form of reading the values out of the page so you can assert the output.
>> Yeah, I mean, that's how I use something like this. I mean, built it home grown a decade ago, but I would take the QA test case, and then I would write it into code.

And I would just do that over and over again until the software was basically just bulletproof, cuz I had hundred of these things covering all sorts of edge cases. That's the problem with unit tests is you can't get the whole application running [LAUGH] and those edge cases worked out.

And like interplaying, you fix one line of code and now three edge cases break, or something like that. Whereas these end up be more difficult to write. There's a lot more pieces involved, but at the end of the day, it really makes your software, I think, bulletproof.
>> It also is a little bit slower to execute than a unit test, right?

And also, if you go back over here, we're actually running this on a real data set, right? So it will be the kinda difficult to provide assertions about the dataset. I could imagine that in the page object, I could write some code to go and reverse engineer all the tags that I inserted inside of the cluster.

And then recover back the original data and then assert the original data. I could kinda imagine that, but it's not really how you wanna do it, right? You really wanna break this down into pieces and test different things in different locations. So the primary reason for the end-to-end test is to make sure that the happy flows connect all the pieces, right?

If you think of your application, your application are lots of independent parts that are glued together with some connections. And while you can easily test individual piece, you're never quite sure whether you hooked up all the connections correctly. And so the end-to-end test is a really good way of proving to yourself that everything is wired together as a unit.

>> But you're right, they do become really slow if you get too many of them.
>> So it's a tradeoff between speed, and something goes wrong. The other kind of a tradeoff that you have over here is when the unit test fails, you are only to testing a small unit.

So you know that the problem is inside of that small unit that you're testing, whether it's a function or a class or a file. But if something fails here, it could be anywhere, right? So these are different tradeoffs that you have to kind of think about when building software.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now