For testing, you mean? I actually think that testing and TDD is one of the most undervalued and underrepresented skills in web development, and so finding high-quality, useful material is very difficult. There are a lot of seemingly different ways to test, and the change in syntax can be really confusing for people trying to get into it. So, with an understanding that it’s extremely difficult to point you to free sources for this, I like Kyle Robinson Young’s videos, and he’s got a 15 minutes introduction here:
I haven’t watched it, so let us know if you found it useful. There are a few things to keep in mind when learning to test code:
-
Test code is just code - When you’re writing tests, you’re writing programs. I think it’s easy to forget this and get an overwhelming sense that you’re somehow trying to master an entirely new beast by writing test code, but it really is just code that uses your other code. It calls your functions, gives that function an input (if applicable), and tells you if it gives the output you’re expecting.
-
All of the different styles of test syntax do the same thing - You’ll see references to difference “assertion styles”. All of these do the same thing, but they communicate the intent differently. Here’s an example of a test using the ShouldJS assertion library:
(5).should.be.exactly(5).and.be.a.Number();
Here’s the same test using ExpectJS
expect(5).to.be(5)
expect(5).to.be.a(Number)
Tape is much more explicit, which may be your preference:
test('numbers!', function(t) {
t.plan(2)
t.equal(typeof 5, "number")
t.equal(5, 5)
}
The point is that it is more important to understand what they’re doing than to memorize the code. You can write tests in straight JavaScript if you want
function equal(a, b) {
return a === b
}
function runTest(message, test) {
if(test) {
console.log('Test passed')
} else {
console.log("Test failed: " + message)
}
}
runTest("5 Should equal 5", equal(5, 5));
runTest("5 should be a number", equal(typeof 5, "number"));
-
Writing tests lets you build quality code faster - Ok, so maybe it will take you an extra couple of days (or even weeks) to bang out your first app using TDD. It requires you to learn some stuff and write all of your code differently. It may feel like you’re not doing anything exciting or important, but think about what happens when you need to make changes to your app. How do you know you didn’t bork something that used to work? How do you know your new feature works at all? You have to test it yourself, manually. Tests let you be much lazier. Also, testing lets you document your code. Here’s the output to my “nightlife coordinator” project that I’ve been writing in Rails:
Api::V1::PasswordResetsController
POST create
with a valid user and email
does not call the authenticate method
finds the user
generates a new password reset toke
sends a password reset email
Api::V1::SearchesController
with a valid token
and valid input
POST #create
returns http success
does not call #authenticate
calls #search_params to get the correct parameters
calls ResultsFormatter#fetch_results with parameters
checks for valid search parameters
PATCH #update
returns http success
calls #add_location_to_plans for current_user
checks for valid update parameters
and if the user was updated successfully
calls #find_or_create_by on the location model
calls #increment_attendence on the found or create location
DELETE #destroy
calls #remove_location_from_plans on current_user
passes the yelp_id param to #remove_location_from_plans
if the plan is removed from the user
calls finds the location by the yelp_id param
calls #decrement_attendence on the found location
and invalid input
POST #create
returns unprocessable_entity status
checks for valid search parameters
calls #error_message
PATCH #update
calls #error_message
and an invalid token
PATCH #update
returns http success
DELETE #destroy
returns http success
Api::V1::TokensController
authentication
finds the user
authenticates the user via the given password
calls the Auth module to issue the jwt
returns a jwt when given valid credentials
Api::V1::UsersController
with valid input
POST #create
returns http success
does not call #authenticate
checks for valid input
creates a new user
passes the valid user params into the new user
GET #destroy
returns http success
ApplicationController
with valid credentials
#current_user
returns the current user
#logged_in?
returns true
#authenticate
returns nil
with invalid credentials
#current_user
returns an error with status 404
#logged_in?
returns false
#authenticate
it renders an error with status 403
auth token is not present
#current_user
returns nil
ResultsFormatter
self#fetch_results
calls YelpSearch#fetch_results with the proper params
the format of the results
should include the coordinates as the property "center"
should return all of the businesses in an array
should return its data in the expected shape
YelpSearch
#search_by_name
calls Yelp::Client#search with all search parameters
#search_by_coords
calls Yelp::Client#search_by_coordinates with all search parameters
#fetch_results
will only call #search_by_name when passed a name
will call #search_by_coords when passed both coordinates and no name
will return an object of results if name input is valid
will return an object of results if coordinate input is valid
will return an error message if nothing is passed
Location
#increment_attendence
should increment attendence count by 1
should save the incremented attendence
#decrement_attendence
should decrement attendence count by 1
should save the decremented attendence
User
validations
requires a password confirmation when creating
requires the password to be at least 8 characters
is valid with valid input
requires a unique email
#downcase email
changes the email to lower case
downcases the email before saving
plans
has an array of plans
#add_location_to_plans
should add the given location to user's plans
#remove_location_from_plans
should remove the given location from the user's plans
#generate_password_reset_token
changes the password_reset_token attribute
calls #urlsafe_base64
Api::V1::SearchesController
#create
with valid search parameters
renders the results in JSON
with invalid search parameters
returns a 422 response
returns the error message
#update
with valid update parameters
and the Location update is successful
returns a status of 202
returns a success message
and the location cannot be found or created
returns a status of 500
returns an error message
with invalid update parameters
returns a 422 response
returns the error message
#delete
with valid delete parameters
on successful deletion
returns a 200 status
renders a success message in JSON
when user's plans do not include the given parameter
returns a 200 status
renders an error message in JSON
Api::V1::TokensController
getting the token
with valid credentials
has a status code of 200
sends the token
with invalid credentials
returns an error when given an invalid password
returns an error when given no credentials
returns an error when the request object format is invalid
Finished in 0.95868 seconds (files took 2.23 seconds to load)
85 examples, 0 failures
This is all generated for me based on how I wrote my tests. When I make a change, I know what works and what doesn’t. When someone clones my repo and spins it up, they know exactly what to expect and where the functionality is. This is why testing is such an important skill to have.
-
The sooner you start learning, the better off you are - It’s much easier to learn to test simple code than to test complex frameworks. Learn how to write good assertions before you need to write mocks and stubs. Start now, accelerate your learning later.