Using Tests for What?!

Bookmark

In the talk I will explain the pains and problems of form validation Then I will explain the mental model of unit tests, and compare it to how we think about form validations.

I will introduce vest with a bit of live coding showing its unit testing syntax.

by



Transcription


Hi everyone, I'm Evitar, I'm a frontend engineer at Meta and one of my passions is working on open source tools and libraries and frameworks for other developers to use and today we're going to talk about one of them. Now this one in particular is a combination between that passion and my other passion which is unitests. I love unitests so much that I actually built a unitesting framework. I mean, not really, not really a framework, we have enough of those, but I did build a form validation framework that takes the syntax and style of unitests and libraries like mocha or just and introduces them into the world of form validation. But before we talk about the VEST itself, let's first try to understand what is the problem with form validation. I mean, we've been doing forms for 20 years now, so what is the problem? And I think there are three main problems in the world of form validation and it all begins with structure or the lack of really. When we write our feature and we add the forms, we don't really know where and how to put the form validation. I mean, yes, we have a change handler and it should come out from there, but inside the forms we usually not just care about the data, we care about the feature and the user experience and then a lot of the validation code gets intertwined with the feature code and then we get a pile of spaghetti code really that's all about both form validation and user experience. And then when we come back to maintain the feature like months later and try to add in stuff or remove stuff or make changes like make fields depend on one another, we have to disassemble all the feature specific code that we wrote and reassembled it in a way that's suitable to our current need. And one other problem is that forms and form validation is really difficult to test. Unfortunately, forms are the interaction heavy parts of our apps. That's where people touch, scroll, type, do everything basically, click and that's also where the money comes from. And because this is the most interaction heavy part of our app, this is also the most bug prone and unfortunately it's really, really hard to test. I mean, everything you want to do in form validation in terms of testing has to go through all the flows. So either user event simulation or you have to go through all the data and it's really hard to test, especially if you have some complex validation logic that you don't want to go all the way through and we end up not testing those and that's a bummer. But what types of form validations are out there? I mean, that cannot be the first form validation library. I mean, we've all used form validation libraries both in the client and the server. So I'll try to get a brief overview of what's there and then we'll understand how VEST is different. And first we have the functional matchers, like basic functions like is email, is number longer than, that basically give us a Boolean response if some value matches some criteria. And this is really simple and this is also really useful, but it doesn't care much about structure, it doesn't help us with maintenance and neither with testing. And this is really useful, but not a full-fledged solution for form validation. Second is that we have the schema validation libraries that take like a next step of what we just described. They take the form of the data, the shape of the data and the shape of the data in its entirety has to conform to some criteria. And this is good mostly for the API level, but when talking about the client, not really. I mean, what do you do when the user interacts with the form, like types inside the username field? Do you validate the whole thing, the whole schema? And also it's really hard to describe complex ideas like fields that depend on one another inside of a schema. So it's really useful, but not necessarily the way we want it to be. And third, we have like in the client framework specific UI form state validation or state management libraries that are specific to the framework and they give you components or event handlers or directives to use inside your app. And then what it does is basically take some control away from you, but gives you all the state management and all the form validation for the form. And then you're pretty much set and it's good. But the problem is, well, it's framework specific, UI framework specific, so we cannot share logic between your client and server easily or between Angular or React or Vue easily. And most of us have at least some apps or some different types of our apps. And also because they take control from you, they put their stuff inside your app, it's really hard to take that control back when you need to bypass some behavior. It's good, but not necessarily, again, the way we want it to be. And how do unit tests fit in? I mean, this is Test.js. Well, a couple of years ago, like six years ago, I started writing unit tests as part of my work. And I realized this. When we write a unit test, we have our unit testing suite that usually starts with a describe, it doesn't have to, but it starts with some top level suite that describes the idea that we're testing our feature or app or whatever it is. Inside of it, we have a series of tests that basically describe what assertion we're making now and what is the error that we're going to show in case of failure. And then we have our assertion, for example, expect and what we expect it to be. And what if we could use that same idea inside of the world of form validation? I mean, it's pretty much the same. When we write a form, we have our top most concept of a form that we want to describe. Inside of it, we have the different fields. So a set of fields that we want to test. And then we just want to make assertion. Now instead of asserting behavior, we want to assert data. So I played around with this specific idea and this is what I came up with. So first we create our form validation suite that's slightly similar to a unit testing suite. You describe optionally the name of the form that you are validating and also you pass in the data from the form that the user just interacted with. And inside of it, you add obviously a series of tests. So you have a test that's very similar to unit testing test, only that it adds one extra field which is the name of the field that you are validating. So in this example, username. And then you pass in the message that the user would see in case of a validation failure. So you do it also both for description inside of the code and also for the user to see. And then what you have is basically an assertion. And instead of using expect, that's not really good for production, I'm using enforce that I wrote. That's much more suitable for form validation both in terms of what it does and how it behaves. So you have enforce, you pass in whatever data you want to. In this case, we pass in the data from the form. And then we add our assertion, for example, is not blank. Now obviously, form validation is not that simple and we can add multiple validations per field. We can add as many as we want and we can actually add more even complex validations, for example, async validations in case, for example, usernames are already taken on a server. And just like in Jest and Mocha, passing in an async function makes the test async. And what if, well, we don't want to go to the server in case the validation is already failing for the field to begin with, like when the username is already failing because it's too short? Well, we can tell it to skip when we have errors for the username field. And what if we still don't want the user to go again and again and again for the same username even if it's valid locally? Then well, we can memorize it and say, well, if the username types the same username again, well, don't go to the server again. And this is just the tip of the iceberg in terms of what VAS can do. It has so many features, so many capabilities, and it answers all the problems that we usually have in form validation. Now one of the neat features is that VAS works with anything, JavaScript basically. So if you use a framework or without a framework or on a server or anywhere really, you can use VAS. And some VAS feature is its full types of support. You can validate changes upon interaction. You can have multiple validations per single field. You have warning validation, for example, password strengths that's not just failing the submission but just telling the user, hey, there's a problem here, and it puts the validation in a different basket. We can have fields that depend on one another, like confirmation and password. We have async validations, and we have memoization. We can group tests, like in a nested describe. We can compose validation rules. We can extend VAS to add more custom enforcements, and everything is structured and framework agnostic. You can install VAS really easily, npm install VAS. It brings in some dependencies that are all specific to VAS, all were written for VAS, so it doesn't bring any third-party stuff. And there is a documentation website, vasjs.dev. There's also a link to the Discord server over there. And if you want to check it out, you can try all of these. Thank you so much, and I'm really excited to see you become a part of the VAS community. Thank you.
11 min
03 Nov, 2022

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic