Writing Tests with Spec

Now that we have a nicely formatted and documented project, let’s see how to write tests for our code. Remember: A project with no tests is like a disaster waiting to happen. Tests ensure that your code works and you don’t feel afraid to change your code because this won’t break existing features. Tests also help others to see the details of how your project works.

Crystal includes a unit testing framework called spec, which is similar to RSpec from Ruby. A spec here means a set of unit tests that goes with a file of code—a file is tested with a corresponding _spec file.

Let’s look at the default mineral/spec/mineral_spec.cr (without the require statement):

 describe Mineral ​do
 # TODO: Write tests
 
  it ​"works"​ ​do
 false​.​should​ eq(​true​)
 end
 end

If you know Ruby, you will recognize describe, it, and should as keywords in the specs DSL:

  • describe lets you group related specs.

  • it is for defining a spec. Put its name between double quotes and the test code between do and end.

  • should is for making assumptions about the spec.

These are all defined using the block syntax we visited in Working with Yield, Procs, and Blocks.

We have one test named “works,” which is obviously a placeholder and will fail. You can run the test by giving the following command in the project root folder:

$ crystal spec

Here’s a screenshot:

images/managing_projects/faulty_specs.png

The difference between what we expected (“should”) and what we found (“got”) is signaled in red as an F for failure: the test “Mineral works” failed.

In general, all tests that are found are run. Every failed test produces an “F”. For every successful test, you get a dot (.). To run only one test file, if you have several, give its name as an argument to crystal spec. To run one test, give its line-number nm like this:

$ crystal spec spec/file_spec.cr:nm

You can create as many tests as you like, as verbosely as you like, without affecting the final project performance. Tests aren’t included in the generated project executable. Also, when you use spec without the --release flag, compile times are really fast and don’t hamper development, even for bigger projects. Use the --release flag only when deploying your app. If you correct the failure in the placeholder test and compare the new output, you should see a green dot and the text “0 failures”.

The code from the previous section that our new code will test is located in the spec folder, and now the test code requires the project’s code. Let’s write some tests for our mineral app.

We should test that the following works:

  • Creating a mineral with the default parameters
  • Creating a mineral with all parameters
  • Generating CSV with the to_csv method
  • Reading the hardness property from module Hardness
  • Returning true with the == operator when comparing the same mineral
  • Returning false with the == operator when comparing different minerals
  • Getting the right value from the kind_of_crystal method

Here is code that tests these functionalities:

 require ​"./spec_helper"
 
 describe Mineral ​do
 # TODO: Write tests
 
 # it "works" do
 # false.should eq(true)
 # end
 
  it ​"creates a default mineral"​ ​do
  min1 = Mineral::Mineral.​new​(108)
  min1.​id​.​should​ eq(108)
  min1.​name​.​should​ eq(​"rock"​)
  min1.​crystal_struct​.​should​ eq(​"unknown"​)
 end
 
  it ​"creates a mineral with parameters"​ ​do
  min1 = Mineral::Mineral.​new​(42, ​"apatite"​, ​"hexagonal"​)
  min1.​id​.​should​ eq(42)
  min1.​name​.​should​ eq(​"apatite"​)
  min1.​crystal_struct​.​should​ eq(​"hexagonal"​)
 end
 
  it ​"creates correct csv format"​ ​do
  min1 = Mineral::Mineral.​new​(101, ​"gold"​, ​"cubic"​)
  min1.​to_csv​.​should​ eq(​"101,gold,2.5,cubic"​)
 end
 
  it ​"gold has hardness 2.5"​ ​do
  min1 = Mineral::Mineral.​new​(42, ​"gold"​, ​"cubic"​)
  min1.​hardness​.​should​ eq(2.5)
 end
 
  it ​"== works for same mineral"​ ​do
  min1 = Mineral::Mineral.​new​(42, ​"gold"​, ​"cubic"​)
  (min1 == min1).​should​ eq(​true​)
 end
  it ​"== works for different mineral"​ ​do
  min1 = Mineral::Mineral.​new​(42, ​"gold"​, ​"cubic"​)
  min2 = Mineral::Mineral.​new​(43, ​"corundum"​, ​"trigonal"​)
  (min1 == min2).​should​ eq(​false​)
 end
 
  it ​"kind_of_crystal works"​ ​do
  min1 = Mineral::Mineral.​new​(42, ​"gold"​, ​"cubic"​)
  (min1.​kind_of_crystal​).​should​ eq(​"cubic"​)
 end
 end

Go ahead and execute the specs and play with the test code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.78.83