JavaScript Test With Jasmine
Testing is a way that you describe your code on how it work and what it can do. Moreover, you can check if your code work like what you are expected or not. Javascript Testing With Jasmine Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any ...
Testing is a way that you describe your code on how it work and what it can do. Moreover, you can check if your code work like what you are expected or not.
Javascript Testing With Jasmine
Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.
Senario
We have form user signup and signin. We want to write jquery for form validation on username, email, and password. we want to check to make sure that username,email,password is not empty, have correct form, follow the length rule. If user input invalid information it will show error message after field lost focus.
Let write our test code on javascript class
For this article I am going to have a Jasmine demo on Rails server. For the article you need have some knowledge with RubyonRails. In this article I am using gem "jasminerice" you can fine more information here. By add gem into Rails APP and run setup command. It will generate some config file and folder for you to work with. Now you will have folder structure as follow like this:
- app |__spec | |__ javascripts | |__ fixture | |__ spec.css | |__ spec.js.coffee
Now first let start writing our first javascript test by creating new file in folder javascripts with name users_spac.js.coffee. And in this file let write our test code:
describe "User class", -> user = null beforeEach -> user = new User(" RathanakJame ", " rathanak@email.com ", " 1234567890 ") it "should trim out spacing for name,email and password", -> expect(user.name).toBe("RathanakJame") expect(user.email).toBe("rathanak@email.com") expect(user.password).toBe("1234567890") expect(user.error_message).toBe("") describe "Name", -> it "should not empty", -> user.name = "" expect(user.validName()).not.toBe true expect(user.error_message).toEqual("User name is empty") it "should be able to use number", -> user.name = "name1234" expect(user.validName()).toBeTruthy() expect(user.error_message).toEqual("") it "should not has space or other symbol", -> user.name = "name space" expect(user.validName()).not.toBeTruthy() user.name = "name@space" expect(user.validName()).not.toBeTruthy() user.name = "name%^space" expect(user.validName()).not.toBeTruthy() expect(user.error_message).toEqual("Invalid User Name") it "should has min length 3", -> user.name = "as" expect(user.validName()).not.toBeTruthy() expect(user.error_message).toEqual("User name too sort") it "should has max length 16", -> user.name = "thelongestnameever" expect(user.validName()).not.toBeTruthy() expect(user.error_message).toEqual("User name too long") describe "Email", -> it "should not empty", -> user.email = "" expect(user.validEmail()).not.toBeTruthy() expect(user.error_message).toContain("Email is empty") it "should has correct email format", -> user.email = "email@email.com" expect(user.validEmail()).toBeTruthy() expect(user.error_message).toContain("") it "should accept Upcase letter", -> user.name = "EMAIL@email.com" expect(user.validEmail()).toBeTruthy() expect(user.error_message).toContain("") it "should not has other symbol", -> user.email = "e#ma&il@email.com" expect(user.validEmail()).toBeFalsy() expect(user.error_message).toContain("Invalid ") it "should has max length 50", -> user.email = "longestemaileverinthewhichmakeinvalidemail@email.com" expect(user.validEmail()).not.toBeTruthy() expect(user.error_message).toContain("too long") describe "Password", -> it "should not empty", -> user.password = "" expect(user.validPassword()).not.toBeTruthy() expect(user.error_message).toBe("Password is empty") it "should has min length 6", -> user.password = "12345" expect(user.validPassword()).not.toBeTruthy() expect(user.error_message).toBe("Password too sort") it "should has max length 15", -> user.password = "password1234578901234567" expect(user.validPassword()).not.toBeTruthy() expect(user.error_message).toBe("Password too long") it "should has correct length", -> user.password = "p@ssw0rd" expect(user.validPassword()).toBeTruthy() expect(user.error_message).toBe("")
Our code above we write to test on User class which have attribue and have attribue name, email, password and methods to check validation on those attribue which check if empty, invalid format, invalid length. In this code we also use jasmine functions, expectations, and matchers such as: describe, beforeEach, expect, toBe, toEqual, toBeTruthy, not., toContain which more information about all of those methods you can find through here
Now let run our test by start the rails server
rails s
And then we can check our code test through link
localhost:3000/jasmine
And then you should see our jasmine report with error.
Let fix our test to make it green
Let create file users.coffee in side directory app/assets/javascripts and write some code as follow here:
class @User constructor: (name, email, password) -> @name = name.trim() @email = email.trim() @password = password.trim() @error_message = "" validName: -> if @name == ' @error_message = "User name is empty" return false else if @name.length < 3 @error_message = "User name too sort" return false else if @name.length > 16 @error_message = "User name too long" return false else unless /^[a-zA-Z0-9_-]{3,16}$/.test(@name) @error_message = "Invalid User Name" return false @error_message = "" true validEmail: -> if @email == ' @error_message = "Email is empty" return false else if @email.length > 50 @error_message = "Email is too long" return false else unless /^[w+-.]+@[a-zd-.]+.[a-z]+$/i.test(@email) @error_message = "Invalid Email" return false @error_message = "" true validPassword: -> if @password == ' @error_message = "Password is empty" return false else if @password.length < 6 @error_message = "Password too sort" return false else if @password.length > 16 @error_message = "Password too long" return false else unless /^([a-zA-Z0-9@*#]{6,15})$/.test(@password) @error_message = "Invalid Password" return false @error_message = "" true
And to make our code accessable in jasmine we need to add require in jasmine spec which locat in app/spec/javascripts/spec.js.coffee to requie some file:
#=require jquery # =require users #=require_tree ./
And now let run our test again Now our test are passed.
Let test our code when it apply on UI
After we successfully create User class for validation user information. Now let test when we apply our code to the form element. To do that we need to have some fixture files. For this article we are going to use two fixture file in app/spec/javascripts/fixture name login.html, signup.html and we need to write html form element inside those file.
<div class="col-md-4 col-md-offset-4 well"> <legend>Log in</legend> <form action="/login"> <div class="form-group"> <label for="session_email">Email</label><br> <input autofocus="autofocus" class="form-control" type="email" name="session[email]" id="session_email"> <span class="error"></span> </div> <div class="form-group"> <label for="session_password">Password</label><br> <input autocomplete="off" class="form-control" type="password" name="session[password]" id="session_password"> </div> <div class="actions"> <input type="submit" name="commit" value="Log in" class="btn btn-primary"> </div> </form> </div>
<div class="col-md-4 col-md-offset-4 well"> <legend>Sign Up</legend> <form class="new_user" id="new_user"> <div class="form-group"> <label for="user_name">Name</label><br> <input autofocus="autofocus" class="form-control" type="text" name="user[name]" id="user_name"> <span class="error"></span> </div> <div class="form-group"> <label for="user_email">Email</label><br> <input class="form-control" type="email" name="user[email]" id="user_email"> <span class="error"></span> </div> <div class="form-group"> <label for="user_password">Password</label><br> <input autocomplete="off" class="form-control" type="password" name="user[password]" id="user_password"> <span class="error"></span> </div> <div class="form-group"> <label for="user_confirm_password">Confirm password</label><br> <input autocomplete="off" class="form-control" type="password" name="user[password_confirmation]" id="user_password_confirmation"> <span class="error"></span> </div> <div class="actions"> <input type="submit" name="commit" value="Sign Up" class="btn btn-primary"> </div> </form> </div>
Now let continue writeing our test code inside app/spec/javascrips/users_spec.js.coffee.
...... ...... ...... describe "Login Form", -> beforeEach -> loadFixtures "login" describe "Email", -> it "invalid when user email is incorrect format", -> email_field = $('#session_email') email_field.validateEmail() email_field.val('email@email') email_field.blur() expect(email_field.next('.error')).toHaveText("Invalid Email") email_field.val('email@email.com') email_field.blur() expect(email_field.next('.error')).toHaveText("") it "invalid when user email is too long", -> email_field = $('#session_email') email_field.validateEmail() email_field.val('longestemaileverinthewhichmakeinvalidemail@email.com') email_field.blur() expect(email_field.next('.error')).toHaveText("Email is too long") it "valid when user email is correct format", -> email_field = $('#session_email') email_field.validateEmail() email_field.val('email@email.com') email_field.blur() expect(email_field.next('.error')).toHaveText("") describe "SignUp Form", -> beforeEach -> loadFixtures "signup" describe "Name", -> it "invalid when user name is incorect format", -> name_field = $('#user_name') name_field.validateName() name_field.val('name of user') name_field.blur() expect(name_field.next('.error')).toHaveText("Invalid User Name") it "invalid when user name is too long", -> name_field = $('#user_name') name_field.validateName() name_field.val('thelongestnameinthehistory') name_field.blur() expect(name_field.next('.error')).toHaveText("User name too long") name_field.val('rathanakjame') name_field.blur() expect(name_field.next('.error')).toHaveText("") describe "Email", -> it "invalid when user email empty", -> email_field = $('#user_email') email_field.validateEmail() email_field.val(') email_field.blur() expect(email_field.next('.error')).toHaveText("Email is empty") it "invalid when user email is incorrect format", -> email_field = $('#user_email') email_field.validateEmail() email_field.val('email@email') email_field.blur() expect(email_field.next('.error')).toHaveText("Invalid Email") it "invalid when user email is too long", -> email_field = $('#user_email') email_field.validateEmail() email_field.val('longestemaileverinthewhichmakeinvalidemail@email.com') email_field.blur() expect(email_field.next('.error')).toHaveText("Email is too long") it "valid when user email is correct format", -> email_field = $('#user_email') email_field.validateEmail() email_field.val('email@email.com') email_field.blur() expect(email_field.next('.error')).toHaveText("") describe "Password", -> it "invalid when user password is sort than 6", -> password_field = $('#user_password') password_field.validatePassword() password_field.val('pass') password_field.blur() expect(password_field.next('.error')).toHaveText("Password too sort") it "invalid when user name is too long", -> password_field = $('#user_password') password_field.validatePassword() password_field.val('thelongestpasswordinthehistory') password_field.blur() expect(password_field.next('.error')).toHaveText("Password too long")
And then let start run our test code again.
Let fix it
Did you notice on our test code above? Did you see some strange methods .validateName, .validateEmail(), .validatePassword()? these methods we need to create for applying our javascript code into form UI. so let start create them inside app/assets/javascript/users.coffee.
...... ...... ...... $.fn.validateName = -> @each -> $(this).blur -> user = new User(@value, "", "", "") if !user.validName() $(this).next('.error').text(user.error_message) $(this).addClass('error_form') else $(this).next('.error').text(') $(this).removeClass('error_form') $.fn.validateEmail = -> @each -> $(this).blur -> user = new User("", @value, "") if !user.validEmail() $(this).next('.error').text(user.error_message) $(this).addClass('error_form') else $(this).next('.error').text(') $(this).removeClass('error_form') $.fn.validatePassword = -> @each -> $(this).blur -> user = new User("", "", @value) if !user.validPassword() $(this).next('.error').text(user.error_message) $(this).addClass('error_form') else $(this).next('.error').text(') $(this).removeClass('error_form')
Let re-run our test.
Yes, they are all passed.
Applying javascript to app UI
To use our code we just add them to element which we want to apply on Ex:
jQuery -> $('#user_name').validateName() $('#user_email').validateEmail() $('#user_password').validatePassword() $('#session_email').validateEmail()
Now are good to go, and let check it out.
Resources:
- https://github.com/bradphelan/jasminerice
- http://jasmine.github.io/edge/introduction.html
Source code: https://github.com/RathanakSreang/jasmine-javascript-testing
Conclusion
This is just an example of using jasmine for testing javascript code. So if you aren’t testing your JavaScript so far, now is an excellent time to start. Jasmine's fast and simple syntax makes testing pretty simple. So let's get write test on you code.
In this world there are no food for free.