CSC/ECE 517 Spring 2013/using fixtures with expertiza

From Expertiza_Wiki
Jump to navigation Jump to search

Using Fixtures with Expertiza

Fixtures are a means with Rails to provide test data. Since Expertiza has several different types of ActiveRecord database objects, Fixtures can be a useful way to accomplish testing.

This page will give a brief overview of fixtures in rails and describe how to use them in Expertiza.

Please remember this is a Wiki page - it's not static! If there's something here that's incorrect or out of date, please help keep it up to date.

What is a Fixture?

The term "fixture" comes from the engineering world (see this definition [1]) and represents some device which can be used to test a system without having to use the system extensively. For example consider how one might test a car tire. Instead of driving a car with the tire thousands of miles, an engineer would create a test fixture which spins the tire over various simulated roads thus providing an easier means of comprehensive testing.

A software test fixture is the same concept - how can we set up a test that would normally be complex in a fixed way.

Fixtures in the context of Ruby on Rails represent test database entries. Once the test database is populated, either manual or automated testing can be done with the resultant database. Fixtures allow the tester to create the same data every time which allows for easier reproduction of bugs and quick regression testing.

How do fixtures compare to other test tools?

It's important to make a distinction between fixtures and seeds in Rails.

Fixtures represent test data. Test data will only be populated during testing and not in a production environment.

Seeds represent a base level of data which is used to start up all applications including those in production. In Expertiza, the most obvious use of a seed data is the initial user admin. However there are many other pieces of non-obvious seed data including certain menu navigation items and other initializations.

According to internet lore, some programmers use seeds to create test data. However this is not good practice as we don't want to have to remember to take our changes out of seeds.rb before making a commit.

When is it appropriate to use fixtures?

Fixtures can be used any time it's desired to have large amounts of data or even smaller amounts that may be somewhat complex.

Fixtures can be used either in an automated sense, with unit and functional test scripts, or in an a manual sense. In the case of Expertiza, the unit an functional tests are not currently using Fixtures, rather they create objects inside the test scripts.

Cucumber is another test tool used in Expertiza. Cucumber will create objects on the fly rather than using Fixtures. It may be possible to use fixtures somehow with Cucumber but it's not a typical configuration.

So for the most part in Expertiza, Fixtures will be used as a way of accelerating manual testing. Creating fixtures and testing with them is also a great way to learn some of the Expertiza database relationships. If you don't create the fixtures correctly you will see strange results during your testing.

Fixture YAML Files

For any rails application, including Expertiza, the fixture definitions are contained in a directory called test/fixtures. Within this directory there is a file for each table in the database with the name of the table and the extension ".yml." So for example, the users table will have a fixtures file called "users.yml."

The yml extension stands for YAML which is a database markup language.

YAML originally stood for “Yet Another Markup Language,” the authors now claim that it stands for “YAML Ain’t Markup Language.” The YAML specification can be found here. [2]

The general concept is that each yml file will include YAML statements which will describe one or more database entries which will be created in that particular table by the fixtures tool.

Basic YAML rules.

A pound / hash sign (‘#’) is used to begin a comment line. Please add comments when modifying the fixture files. Even if there are no comments, and you personally understand what's going on, go ahead and add a comment or two. Some future developer may thank you.

YAML relies heavily on indenting of non-comment lines to establish database relationships. Only spaces, never tabs, may be used to indent YAML files. While this may seem annoying, it’s done to ensure compatibility between different environments which treat tabs in different ways.

An ActiveRecord object is defined with YAML in the following manner:

objectName:
  Attribute1: value
  Attribute2: value
 …
 AttributeN: value

Thus in the example above we would create an object called "objectName" in whatever table the file refers to. This "objectName" is not stored in the database and does not represent any field or attribute of the ActiveRecord object. It is only used within the fixtures generation program as a reference to this particular object. As we will see later this is a useful characteristic because we can use the identifier in other yml files to reference the same object much more easily than if we had to use the object IDs which are stored in the SQL database.

Again the very basic rules of YAML are: Comment lines start with a '#' character. An Object name starts in the first column. Attributes of the object are indented with spaces (not tabs)

More Advanced YAML

Using Fixtures.identify()

Here's an example from the file users.yml of creating a user:

instructor1
 name:instructor1
 parent_id: <%= Fixtures.identify(:admin) %>
 fullname: instructor1_fullname
 password: <%= Digest::SHA1.hexdigest("instructor1") %>
 password_salt: ""
 email: instr1@mailinator.com
 role_id: <%= Fixtures.identify("Instructor_role) %>
 email_on_review: 1
 email_on_submission: 1

Note the parent_id and role_id fields. These are references to other objects which in the SQL database would be a large, effectively random number. When we run our MVC code, these relationships get created explicitly - I'm going to make sure that a given user has a parent and role_id that I specify. But here in the YAML file we must link the records together in a different way since we are not running the MVC code.

To do this we use the Fixtures.identify method. We now get to use that object identifier which is in the first column (and is not populated in the SQL database). This object identifier is used to index and find the numeric value using the Fixtures.identify() method.

The example above is typical of how we can create relationships between different database tables. Note, for example, that "instructor_role" is from a different table (roles.yml) and refers to an object created in that file.

Fixtures.identify() is one of the most used YAML features in Fixtures and you will see it again and again.

Other YAML Tips and tricks

YAML gives us several ways to enhance creating objects in the database. We'll touch on the most commonly used in Expertiza.

Arrays

A YAML array can be used to store several objects or attributes for use later in the yml file. For example, below is an array of objects of the user class.

<% instructor_list = [] %>
<% instructor_list << Fixtures.identify(:instructor1) %>
<% instructor_list << Fixtures.identify(:instructor2) %>
<% instructor_list << Fixtures.identify(:instructor3) %>

I can now reference the array with an iterator as described below. In the case above we are referencing objects but we could just as easily reference attributes like so:

<% comment_list = [] %>
<% comment_list << "Great work, best ever!"  %>
<% comment_list << "Mediocre effort, you can do better." %>
<% comment_list << "Barely acceptable." %>

Iterators

YAML provides iterators to create multiple objects in a short amount of code.

The general format of the iterator is:

<% for [var] in [range] %>
other statements
<% end %>

Within the other statements the iterator value can be substituted. For example, the following code would create 10 identical objects:

<% for x in 1..10 %>
object<%= x %>
   attr1=value
   attr2=value
<% end %>

Iterators can be combined with arrays for interesting results:

<% for x in 1..10 %>
object<%= x %>
   attr1=comment_array[ i%3 ]
   attr2=i
<% end %>

You will see many examples of combining iterators, arrays and Fixtures.idenfity() in the Expertiza codebase. When used together these are a powerful means to create test scenarios with large numbers of database entries.

Using Rake to Instantiate Fixtures

Like many things in Rails, the rake tool is used to create the fixture data.

The easiest way to do this is to start by cleaning out the database. The following command will delete all data in the database and populate with only the seed data:

rake db:reset

Now we want to generate the fixtures. Type:

rake db:fixtures:load

The fixtures tool will run the various yml files and create the data. Now you may find an error - if the file will not parse correctly an error is generated. In some cases the error messages are cryptic but they generally do provide the correct line. Realize that someone else may have introduced a problem since not all developers use Fixtures.

Once you get a clean fixtures load you can start the rails server like so:

rails script/server

Log in to the application and you'll see your data. You're now ready to start testing away!

Now when you run into that strange problem you'd like to debug, you can begin from exactly the same point by re-running the procedure and having the database in the same condition.

Using the DBConsole Tool

One thing you'll notice when using fixtures is that you're manipulating the SQL database directly. In other words things won't always be going through the Model/View/Controller code structure. Because of this it can be useful to use the SQL Database console in some cases.

For example, you might want to take a look at the DB console after creating some database entries in Expertiza the 'normal' way -- by logging in as a user, creating and submitting reviews and so on. In this way you can see the details of how the various tables are related to one another which may be helpful in creating your fixtures.

Output of the tables command in dbconsole.
Output of the tables command in dbconsole.

To access the DB console from the linux shell type:

ruby script/dbconsole

You will see the prompt:

mysql>

From this prompt there are several interesting things to do. The most basic are looking at the various tables available, the fields in those tables, and looking at the records stored in the database.

Often things will scroll off the screen (especially if you are not using X Windows) so you will want to define a pager:

mysql>pager more
PAGER set to 'more'
mysql>

Now the pager 'more' will be used to filter the output from DB Console so you can read it.

First let's take a look at the tables that exist.

mysql>show tables;

Will show you a listing of tables similar to the screen shot. Compare this list of tables to your reference material such as schema.rb or other documentation.

Once we have seen all of the tables we might want to take a look at the various fields inside those tables. As you might expect this is a very straightforward command:

mysql> show fields from [your table];

Don't forget the trailing semicolon after all SQL commands. The screen shot below shows how this will look. Again this should be no great surprise if you've looked at other documentation but it's always interesting to get another view of how things fit together.

Output of the show fields command in dbconsole.
Output of the show fields command in dbconsole.

Now we can get to the business of examining the actual data. To do this we will use a database query. In SQL, database queries usually begin with SELECT. SELECT will 'select' as its name implies, some set of data from the database.

The general format of select is SELECT [one or more fields] from [table name]; This command will select all records of the specified table and show the fields you specified. For example, to select all fields from the user table you would enter:

mysql>select * from users;

This will cause lots of data to be thrown to the screen which may not be that interesting. To limit the data you can choose only certain fields, for example:

mysql>select name,handle from users;

Will print out just the names and handles of all users.

Now we may want to do some filtering. SQL SELECT statements allow qualifying the data selected. How about choosing users who have a handle defined since only some users do?

mysql>select name,handle from users where handle is not NULL;

Will print out only those users with a handle.

This only scratches the surface of what SQL can do, but it should get you thinking about how you can use DBconsole to debug your fixture definitions. For more information on SQL Select click here[3]