Talia Testing

The Talia Core contains a set of unit tests for all important classes. These should be kept up to date during development, new tests should be added when the code changes or when bugs are found.

Rails Test in general

To learn about testing in Rails, you can start with the official testing guide, and there are a lot of different tutorials to be found on the web.

Rails tests come in three flavours:

  • Unit tests, which test the behaviour of the model objects. Each test will typically call one of the methods of the object and see if works as expected
  • Functional tests, which test the behaviour of the controllers. A functional test will usually make an HTTP call to a controller and see if the result is as expected. (e.g. if the database changed accordingly, if the HTTP result code is as expected, if the HTML looks like expected). Talia contains some basic functional test for the "default" controllers
  • Integration tests, which work like functional tests, but test a complete sequence of operations. For example an integration test may test the whole workflow of placing an order in a web shop. (Integration tests are not currently included with Talia).

Rails unit tests use fixtures, which are yml files that contain the contents of the database used for the tests. Rails will usually load the fixtures into the database, and reset the database after each test.

Peculiarities in Talia

Talia contains some additional functionality for tests. The file test/test_helper.rb contains the code for starting up Talia for the tests. It also contains some house-keeping helpers, like for finding fixture, files for resetting the data store and for deleting the "data" directory for the tests when done. These are designed to be used with the Talia Core unit tests.

The main tests helpers are found in lib/talia_util/test_helpers.rb. These helpers can also be included in other projects (e.g. in the application unit tests.

Suppressing fixtures

The usual fixtures in Rails tests fill the database from the *.yml files in the fixtures directory, and reset the SQL database after each test method. While some of the Talia tests use fixtures (particularly the active_source_test.rb), this behaviour is often undesired:

  • The fixtures load only the database content, not the RDF store
  • The database will always be reset, while RDF will not
  • Since the database will always be reset, records cannot be set up using the one-time mechanism (see below)
  • In general fixtures are easy to get things started, but decouple the setup from the test itself.

Talia gives you the possibility to completely disable the fixtures. This means that the fixtures will not be loaded and Rails will not attempt to reset the database after each test method. To disable the fixtures, put this in your test class:

suppress_fixtures

Flushing the RDF store

When you do use fixtures, you may want to "flush" your RDF store after each test. Although the records that come from the fixtures will not have any RDF associated, you may want to create new records inside your tests. The records created inside the tests will have RDF; but when the test method exits, the fixture mechanism will delete the database record for that source and not the RDF.

Thus, when you create new sources in your test methods, you will have "leftover" RDF triples in your store that aren't matched in the database. To avoid this, you can simply clean out the RDF store before and after each test method:

def setup
  TaliaUtil::Util.flush_rdf
end
  
def teardown
  TaliaUtil::Util.flush_db
end

Note that creating new sources in each test and flushing the RDF store for each test will be quite expensive. Most of the time you will want check if its not better to disable the fixtures and use the setup_once mechanism instead (see below).

One-time setup of variables

Instead of using fixtures, you can create Source objects directly in the tests. However, if you need an object in each test, saving and loading it from the database can become very expensive.

To avoid this kind of overhead, there is a possibility to set up a variable only once for all test methods in a class. It goes like this:

...
def setup
  setup_once(:my_var) do
     # create a value
  end
end

The setup_once helper will check if the variable @my_var already exists. If not, it will execute the block and assign the result to the variable @my_var. If the variable already exists, nothing will happen and the variable will remain unchanged.

Not that to create database objects with this methods, be sure to suppress the Rails fixtures. Otherwise the fixture mechanism will reset the database after each test (even if you don't load any fixture files!).

Assertions and other helpers

The TaliaUtil::TestHelpers class contains some additional assertions that can be used in Talia unit tests. One of the most useful is the assert_property helper, which will assert if the given property has the expected values:

assert_property(@source.rdf::test, "value", @other_source)

The above will check if the collection returned by @source.rdf::test has the correct type and contains the value value and a source equal to @other_source in any order.

How to write unit tests

Every time you create a new model, you should also create a new unit test file, with the name <my_new_model_class>_test.rb, and a class name to match the file name. An easy way to get an "empty" unit test is to copy the boilerplate code from another test, change the name, and fill in your own test methods. However, don't forget to change the name of the test class inside the file. The tests will not complain if you have the same test class defined in two different files and will not give you an error - however you will get strange errors and failures.

If you create a new model in your app/models directory, put the test in test/units. If you create a new TaliaCore? class the tests will go in tests/talia_core (or tests/talia_util - the subdirectory in tests should match the subdirectory in lib).

Then you should write simple, clear test methods, each of which should test one behaviour of one method of a model object. You can have a look at the active_source_test.rb or the source_test.rb file. The first has a comprehensive test suite for the ActiveSource class, the latter (while smaller) also shows you the use of the setup_once mechanism.

A typical test method will look something like this:

# Test find :first
def test_find_first
  source = Source.find(:first)
  assert_kind_of(Source, source)
end

As you can sse, the test will call some method, and then assert if the result of the call is as expected. It is a good practice to limit each unit test to a single aspect that you want to test and to not try to test multiple things in the same test method.

How to write functional tests

A functional test should be created for each controller. Functional tests are very easy to write, especially in the current versions of Rails. A simple test for a "GET" action may look like this:

def test_show
  get(:show, :id => @collection.id)
  assert_response(:success)
  assert_select('ul#collection_order') { assert_select('li.item', 5) }
end

The get method in this example will send an HTTP GET request to the controller's show action, with the id set to the given parameter. Note that the functional test will always work on the controller with the same name as the test class - so there is no need to specify which controller you will be working with.

The assert_response will check the HTTP response code that was returned (you may either use a symbolic name like :success or a numeric code, like 404). Finally, the assert_select will allow you to check the HTML that was returned. The assert in this case will assert that there is a <ul> tag, with the id collection_order and that it contains 5 <li> tags that have the HTML class item.

For another example, this would be a functional test for a create action:

def test_create
  assert_difference('TaliaCollection.count', 1) do
    post(:create, :talia_collection => { :title => 'Meee new title'})
    assert_redirected_to(:action => 'index')
  end
  new_collection = TaliaCollection.last
  assert_equal('Meee new title', new_collection.title)
end

As you can see, this is a call to the create method of the controller, and it uses HTTP POST instead of GET. For all of the post/get/... calls in your tests you can specify the parameters as a hash, there will be a talia_collection parameter which has a title inside.

This test uses the assert_redirected to see if the create action redirects the browser back to the index page. In addition, it uses assert_difference to check that we have 1 more TaliaCollection after the create call than we had before.

After the action itself was run, the tests gets hold of the newly created object, and checks if the parameter was correctly passed to the new object.

When to write new tests, practices

Ideally, tests are written before writing the code. In this way, they are a kind of specification for the behaviour of the code. It is also good practice that, when you find a bug, to write a test before fixing it. This way the test will fail before the fix, and pass after. It will show you if your fix works, and prevent regressions later. Test are also a good thing while investigating bugs.

If you have some code without tests, and you start "trying it out" manually, this is a very strong sign that you should write automated tests instead.

Finally, it is a good idea to run all the tests before checking anything into the reposirotry, especially the talia_core one.