Skip to content

Testing

One of the benefits of using the Athena Framework is testing is considered a first class citizen. Both the framework and the components themselves provides testing utilities to help ensure your code is working as expected.

TestCase#

At the core is the Athena::Spec component, with ASPEC::TestCase being the primary type. ASPEC::TestCase provides an alternative DSL for creating tests compliant with the stdlib's Spec module.

Note

ASPEC::TestCase is NOT a standalone testing framework, but is fully intended to be mixed with standard describe, it, and/or pending blocks depending on which approach makes the most sense for what is being tested.

The primary benefit of this approach is that logic is more easily shared/reused as compared to the normal block based approach. I.e. a component can provide a base test case type that can be inherited from, a few methods implemented, and tada. For example, AVD::Spec::ConstraintValidatorTestCase.

struct ExampleSpec < ASPEC::TestCase
  def test_add : Nil
    (1 + 2).should eq 3
  end
end

Tip

The ASPEC::TestCase::DataProvider and ASPEC::TestCase::TestWith annotations can make testing similar code with different inputs super easy!

Testing Services#

Testing a type/service is best done in isolation, using mocked versions of its dependencies to ensure that specific type is working as expected. In most cases this can be as simple as defining a private class that includes/implements an interface along with additional inputs for asserting it was called as expected. In other cases, the related component may provide these out of the box, such as:

Checkout the Spec namespace of each component in the API Reference for more examples.

Testing Controllers#

While testing a service in isolation is a good starting point; it does not make the most sense for all types of services. A perfect example of this are ATH::Controllers. Controllers are best tested in conjunction with the various moving parts that make them function.

To make this as easy as possible, the framework provides ATH::Spec::APITestCase and provides many helpful HTTP related expectations.

require "athena"
require "athena/spec"

class ExampleController < ATH::Controller
  @[ARTA::Get("/add/{value1}/{value2}")]
  def add(value1 : Int32, value2 : Int32, @[ATHA::MapQueryParameter] negative : Bool = false) : Int32
    sum = value1 + value2
    negative ? -sum : sum
  end
end

struct ExampleControllerTest < ATH::Spec::APITestCase
  def test_add_positive : Nil
    self.get("/add/5/3").body.should eq "8"
  end

  def test_add_negative : Nil
    self.get("/add/5/3?negative=true").body.should eq "-8"
  end
end

# Run all test case tests.
ASPEC.run_all

Testing Commands#

Similar to controllers, commands also have additional moving parts that need to accounted for when testing. The ACON::Spec::CommandTester type can be used to simplify this:

describe AddCommand do
  describe "#execute" do
    it "without negative option" do
      tester = ACON::Spec::CommandTester.new AddCommand.new
      tester.execute value1: 10, value2: 7
      tester.display.should eq "The sum of the values is: 17\n"
    end

    it "with negative option" do
      tester = ACON::Spec::CommandTester.new AddCommand.new
      tester.execute value1: -10, value2: 5, "--negative": nil
      tester.display.should eq "The sum of the values is: 5\n"
    end
  end
end