Now that you have an awesome testing environment with Testem set up, you need to actually start writing tests. This is where people begin to feel daunted. “Oh no, I need to learn another library” comes to many people’s minds and because this library doesn’t add any cool features to your site or application, it doesn’t seem like much fun. We need to brush that aside, though, because testing libraries tend to have very, very simple APIs and can be learned within a couple hours of tinkering. Let me prove it to you.
Suites and Specs
Jasmine has a few main global functions in its arsenal. These functions are global mostly so that the code is simpler to read. You can actually nearly read it like a sentence. The first function we’ll talk about is describe
. This function is used to organize your tests into suites. A suite is just a fancy name for a collection of tests so that you can organize your tests into related blocks. Here’s how describe
looks in action:
1 | describe("A description or subject for your test suite", function(){ |
As you can see it takes two arguments: A string that is used to identify a suite and a function that contains the actual test code. The string is used in the test runner to hierarchically display which tests passed and which ones failed. The function can contain any code you want it to contain; however, you need to use specific Jasmine-supplied functions in order to get anything to show up in the test results, as we’ll explain in the next section.
Suites can be nested inside of each other too. This allows you fine-grained organization of code into hierarchical structures. I usually have an initial describe
block for an object and then have nested describe
blocks for each of its methods, like so:
1 | describe("Some Object or 'class'", function(){ |
Now that we have the code organized into suites, which generally represent the nouns, we need to write some tests (aka specs), which are the verbs. We do this with it
. This is how it
looks:
1 | describe("This thing", function () { |
As you can see, it
is nested within a describe
block so that the spec’s verb can be associated with a noun that commits the verb. So, describe
is where you say what the object is that does something and it
is where you say what it does. Within it
is where you actually test to see if the verb was completed successfully. We’ll discuss how to do that in the next section.
Before we take a look at that, though, take a close look at the comments I placed inside the it
block. I separated it into two different sections: setup and checking. First you set up and run the necessary functions. Then you test to see if everything worked out the way it should have. This is the “standard” way of doing a test and is the pattern you should always follow. Obviously, though, if there’s nothing to set up, just skip that part and start checking.
What to expect
When You’re Expecting
As I just said a moment ago, within it
is where you do your actual testing. Essentially, you are just checking to see if the outcome is the same as you expected. Jasmine uses expect
as the name of the function that is used to test the expected outcomes. expect
takes a single argument, which can be any value, and then returns an object that has a bunch of methods called matchers. The matchers are what test the value to determine if it was correct. It’s hard to explain this without code, so take a look:
1 | describe("This thing", function () { |
We passed returnValue
into expect
and it gave us an object with the matcher methods on it. We chose to use the toBe
matcher and passed in 2 as the argument. toBe
just compares the value given to expect
with the value given to it using ===
. Essentially this is what is happening for Jasmine in this situation:
1 | if ( returnValue === 2) { |
If you don’t use expect
and a matcher, your tests will always be considered passing tests, unless there is an error thrown (there is at least one exception, which I’ll discuss later). If you really want to be sure your tests are passing/failing, then you need to use expect
. You can use expect
as many times as you want per it
block, but you should try to keep them minimal. If you need to call expect
a lot, it probably means that the functions you’re testing are doing too many things. If any of the expect
s fail, the entire spec will fail.
There are a ton of matchers, and it doesn’t really pay to go over them all here. toBe
is a very common one and is definitely one of the easiest to understand and can be used in most situations, but you should see the Jasmine documentation for the rest of the matchers. You can also create custom matchers, but I won’t go over that here. They just allow you to write matchers that simplify how you write your tests so that they are more domain-specific.
Setup and Cleanup
Two more of the functions that Jasmine provides are beforeEach
and afterEach
. These aren’t necessary, but can help keep your code DRY. You use them inside of your describe
blocks and before your it
blocks. They each take a function as their sole parameter, and these functions are run before/after each of the specs, including the specs nested inside deeper describe
blocks. This way, if you have some common setup or teardown procedures, you can place them inside one of these functions and only write it once instead of repeating it within each of your it
blocks. Here’s an example:
1 | describe("This thing", function () { |
What’s this
?
The final thing we’ll be talking about today is the this
keyword. You can of course use closures and local variables for containing all of your data that will be passed around to each of your functions, but this isn’t the only way. Each function you pass around (such as to beforeEach
, it
, and others) is run in the same context. This means that you can define something in beforeEach
with this.someObj = …
and you can access that object within it
with this.someObj
. It’s entirely up to you which technique you use, but I still wanted you to see your options so you have a choice.
1 | describe("Some Object", function () { |
Conclusion
That’s all I’m going to talk about today. Next week we’ll discuss a few more things that Jasmine offers us, such as Spies, jasmine.any
, and asynchronous testing. I hope that you are looking forward to it and spend a little time practicing your Jasmine unit testing before we get into it. God bless and happy coding!