There are three different parts needed to get started with JtestR. Two of them are usually necessary for every project.
JtestR and Ant
The first step to get started with JtestR is to download the binary jar file from http://dist.codehaus.org/jtestr. The file is quite large, since it contains several interesting libraries. Once you have downloaded the jar file, the only thing you need to get started is to add it to your classpath for the project you want to test, and then make sure you define the tasks necessary for JtestR to work. There are two tasks you might be interested in adding to your build.xml. The first one is for general testing:
Make sure that change the classpath variable of the taskdef to point to the real jtestr.jar. Now, when you run the test target in Ant, jtestr will check in several directories for the place where you have put your tests. Now, when you run the test target in Ant, jtestr will by default look for and run tests from a directory named "test" and its subdirectories.
If you want to specify a different directory for your tests, use the "tests" property of the jtestr task:
Where my_tests is a directory relative from the build.xml file. The jtestr task takes these arguments:
- failOnError (boolean, default is true): Whether JtestR should fail the Ant build when the testing fails.
- port (int, default is 22332): The port to try checking for a JtestR server on.
- tests (String, default is "test"): The directory to look for JtestR configuration file and tests.
- groups (String, default is ""): The groups to run, separated by comma. If not specificed, runs default groups.
- logging (String, default is "WARN"): The logging level, valid values are: "NONE","ERR","WARN","INFO","DEBUG"
- configFile (String, default is "jtestr_config.rb"): The name of the config file. Note that this is not a path, just the name of the file itself.
- outputLevel (String, default is "QUIET"): The unified result handling in JtestR has different levels of verbosity. Valid values are: "NONE","QUIET","NORMAL","VERBOSE","DEFAULT"
- output (String, default is "STDOUT"): The place output from test runs should end up. This can be any Ruby code. The resulting object needs to be IO-like.
You can also set a system property to use a specific test. This isn't one of the options to configure, but instead is only a Java system property:
- jtestr.test (String, default is ""): The name of a specific test to run. If empty it isn't used.
If you set this as an Ant property, it will be picked up and inserted as a system property too.
If you are running your tests often - as you should - you will soon realize that it takes some time for JtestR to get started. This is because the JRuby runtime is a bit heavy weight to start. To solve this problem, there is a second ant task available that starts a background server. If a background server is started, the regular jtestr task will automatically use that instead. Both jtestr and jtestr-server takes an optional port to use for the server:
The jtestr-server task takes these arguments:
- port (int, default is 22332): The port to start the JtestR server on.
- runtimes (int, default is 3): How many runtimes to cycle through for testing.
- debug (boolean, default is true): Should the server write information about each connection or not?
Note that the server also takes an optional argument for how many JRuby runtimes to start. In most cases 2 or 3 is good enough. It will take some time for the server to start, but once it's started it will cycle through runtimes so you should be able to start test runs very quickly. You need to keep the test-server ant task running to keep the server running. The task also provides some logging for every time it runs a test task.
And that's really all you need to know on how to get started from the Ant side of things. The next step in getting started is to look at how to get started with Maven integration, or how to get started writing tests with JtestR.
JtestR and Maven
JtestR currently only supports Maven2. It should be fairly simple to integrate it with Maven 1 too, though, since the Ant tasks are available. These instructions are only for Maven2. To test your project using Maven2, you need to add this to your pom.xml:
That's the basic parts needed for standalone test execution. Exactly like with Ant, there are several configuration values you can add, though. These are added in the regular manner of Maven plugin configuration:
The configurations supported are:
- failOnError (boolean, default is true): Whether JtestR should fail the Maven build when the testing fails.
- port (int, default is 22332): The port to try checking for a JtestR server on.
- tests (String, default is "test"): The directory to look for JtestR configuration file and tests.
- groups (String, default is ""): The groups to run, separated by comma. If not specificed, runs default groups.
- logging (String, default is "WARN"): The logging level, valid values are: "NONE","ERR","WARN","INFO","DEBUG"
- configFile (String, default is "jtestr_config.rb"): The name of the config file. Note that this is not a path, just the name of the file itself.
- outputLevel (String, default is "QUIET"): The unified result handling in JtestR has different levels of verbosity. Valid values are: "NONE","QUIET","NORMAL","VERBOSE","DEFAULT"
- output (String, default is "STDOUT"): The place output from test runs should end up. This can be any Ruby code. The resulting object needs to be IO-like.
You can also set a system property to use a specific test. This isn't one of the options to configure, but instead is only a Java system property:
- jtestr.test (String, default is ""): The name of a specific test to run. If empty it isn't used.
You can use another Maven goal to start a background server, just like with Ant. The best way of starting it is by running this command:
You can provide two other pieces of configuration that is only for the background server:
- runtimes (int, default is 3): How many runtimes to cycle through for testing.
- debug (boolean, default is true): Should the server write information about each connection or not?
The port argument affects both the test goal and the server goal.
There might be a problem with a dependency in the Maven build on a non-standard JRuby complete. This will be fixed in future revisions, but for now the best way of handling it is to download the file from http://dist.codehaus.org/jtestr/jruby-complete-r6947.jar, and install it locally using this command:
Also note that JtestR work as expected in projects using submodules. Look in the source distribution, in the examples/multi_maven_project for an example that shows how it could look.
If you define your project dependencies in the Maven POM you do not need to add these dependencies to the classpath in the configuration file. The default classpath in a Maven2 project will instead be the default test classpath that Maven uses.
That is really all there is to the Maven2 integration.
JtestR and JUnit
From JtestR 0.3, you can also use a JUnit harness to run your JtestR code. The reasons you would want to do this are several. Maybe the most interesting one is for IDE integration. If you IDE supports JUnit, it's extremely easy to get it to work with JtestR. If you want standard XML output from your tests, the JUnit integration makes this possible too.
Too configure for JUnit XML output, you can do it like this:
As you can see, this example uses the JtestRSuite class, and this is the way you would configure any JUnit integration. The JtestRSuite takes care of everything for you, and you can configure the output just as you would any other JUnit test run.
There are a few configuration options you can send to the system, but since JUnit doesn't provide a standard way of configuring suites, this is done through system properties for JtestR. The available system properties you can set are these:
- jtestr.junit.tests (String, default is "test"): The directory to look for JtestR configuration file and tests.
- jtestr.junit.logging (String, default is "WARN"): The logging level, valid values are: "NONE","ERR","WARN","INFO","DEBUG"
- jtestr.test (String, default is ""): The name of a specific test to run. If empty it isn't used.
JtestR command line tools
If you want to, you can also run JtestR independent of any specific build system. This will pick up and use the same configuration as the Ant and Maven2 configuration, but might be of use in certain circumstances when you want to try out more interesting tools on JtestR. The command line interface looks like this, assuming your classpath is set up correctly:
All command line arguments are optional:
- port (int, default is 22332): The port to try checking for a JtestR server on.
- tests (String, default is "test"): The directory to look for JtestR configuration file and tests.
- logging (String, default is "WARN"): The logging level, valid values are: "NONE","ERR","WARN","INFO","DEBUG"
- configFile (String, default is "jtestr_config.rb"): The name of the config file. Note that this is not a path, just the name of the file itself.
- outputLevel (String, default is "QUIET"): The unified result handling in JtestR has different levels of verbosity. Valid values are: "NONE","QUIET","NORMAL","VERBOSE","DEFAULT"
- output (String, default is "STDOUT"): The place output from test runs should end up. This can be any Ruby code. The resulting object needs to be IO-like.
- groups (String, default is ""): The groups to run, separated by comma. If not specificed, runs default groups.
You can also set a system property to use a specific test. This isn't one of the options to configure, but instead is only a Java system property:
- jtestr.test (String, default is ""): The name of a specific test to run. If empty it isn't used.
The main reason for using the standalone runner instead of a more simple build integration would be to facilitate using a test coverage framework like Emma. You can do this from Ant using something like this:
JtestR testing
When using JtestR for testing, you have several choices for test engine. The Ruby libraries Test::Unit, RSpec and Expectations are all included, and you can write Test::Unit tests using either the regular way of doing Test::Unit, or with dust syntax. This guide will quickly show you examples using each of these technologies. Unless you change it in the configuration file, all files ending with _spec.rb will be run using RSpec, and everything else will use Test::Unit. Of course, your existing JUnit and TestNG tests can also be integrated and run together with the rest of the tests in JtestR.
You will notice that the way Ruby test frameworks usually work is that you have a test directory that contains all the tests. They are usually organized in a directory structure with three different base directories: unit, functional and integration. In JtestR, all directories named unit will run first, then the directories named functional, then the integration directories, and finally all files that hasn't been run yet. You will see that as JtestR reports what it's doing. You will also notice that Test::Unit, RSpec, Expectations, JUnit and TestNG tests are run separately. This is because RSpec allows you to change output formats in different ways from the other engines.
Lets first take a look at a few tests for java.util.HashMap, written using classical Test::Unit style:
As you can see, this code looks quite a lot like typical JUnit code. You have a setup method where a new HashMap is created. This gets called for every test. Test::Unit will use all methods that begin with test_ and run these. Test::Unit also includes several standard assertions that can be used to check expectations. You see three of them in the example above, assert, assert_equal and assert_raises. Available assertions can be found in the module Test::Unit::Assertions:
Note that for testing exceptions, you should use assert_raise and assert_nothing_raised. The methods assert_throws and assert_nothing_thrown is for a different language feature in Ruby, that really doesn't have a counterpart in Java.
Testing with Test::Unit can also have a teardown method that runs after each test. All documentation you can find on Test::Unit online is as applicable for testing Java code as for testing Ruby code.
Many developers feel that the way Test::Unit requires you to specify tests is a bit unnatural and verbose. Jay Fields created dust to get around this problem. You can use dust anywhere in JtestR instead of regular Test::Unit syntax. Note that you still use the same assertions, setup and teardown methods with dust. The only thing that is different is how you define your tests:
And that's really all there is to dust. Behind the covers, a Ruby class wil be generated based on the file name this code resides in.
RSpec is a bit more different. The framework has a different goal than Test::Unit, but it works very well for most testing tasks. In JtestR all files ending in _spec.rb will be run with RSpec. You can change which files are used with the configuration file, though. A typical RSpec test file contains descriptions about something, and examples of how the behavior of it should work. In practical terms it looks like this:
The structure of a typical RSpec specification uses describe to start a block describing something. The description of the thing under test should more or less match the common thing for all examples in that group. Each example is described with the it-method. You use before(:each) to do something before every test, and before(:all) to run something once, before all the examples are executed. There are corresponding after-methods. If you don't give a block to the it-method, you have created a pending test. That's legal and will be reported separately when run through JtestR.
The basic ways of checking values in RSpec depend on the should and should_not methods. You send them so called "expectation matchers", and that is the test in question. The combination ends up being quite readable and natural. The matchers in RSpec are quite flexible, and there are many of them. The best way to find out about them is to look at the RSpec site at http://rspec.info and read their documentation.
JtestR also have support for RSpec stories. To use that, first read up on RSpec stories, then check out the example in the JtestR test suite (in test/stories).
The Expectations support is brand new, and allows you to decide if you want to use Expectations instead of any of the other formats. You need to specifically point this out in the configuration file for it to work, but after you have done that it works exactly like the Test::Unit and RSpec support.
JtestR gives you some helpers that might be very useful in certain circumstances. Most of these are documented elsewhere, but one thing that can be good to know is the J module. If you get annoyed at having to write the fully qualified name of a Java class all the time, or having to import it, you can use the J-module:
By default the J module will search java.lang and java.util. If you want to change this, it's quite easy to do:
If you want to get J back to the default search list or remove all the cached constants that J have created for you, do it with reset:
This concludes the getting started part for JtestR testing. For more information see the examples and documents about specific features.