Introducing Generic Fixture for FitNesse

I have been using FitNesse (A wiki based integrated testing framework) for last few years to create automated integrated acceptance tests for my back end Java based and legacy C/C++ applications. It has helped us tremendously towards our efforts to adapt to Test Driven Development (TDD). FitNesse has really helped our development team to find & fix bugs much earlier and our QA team to validate them pretty quickly. For beginners I will recommend reading the documentation on its official web site http://fitnesse.org/ for information on downloading, installing and setting up FitNesse. FitNesse provides various “Test Table Styles” (or fixtures) to write your tests. Each Table/Fixture Style has its own usage and purpose.

For testing business logic in my back end applications I mostly used ColumnFixture, TableFixture and RowFixture for defining inputs and output data sets. All I needed to write was a thin adapter or mapper Java class that extends any of these fixtures. That thin adapter Java class just gets input data from wiki and use that data to make a call to my back end application. At the end of the call it just makes the result available for FitNesse to display on wiki page. This thin adapter class is fully Fixture aware and developer of this class must know how to map data between back end application and FitNesse front end wiki page.

So far so good no major complains here, it worked great for us.

But I found this requirement to write the thin fixture adapter class for each new transaction a little annoying and have always toyed with the idea of building a generic Table/Fixture, one that will not require a developer to understand and be aware of the various fixtures coding conventions. In other words a fixture/table style where testers can write acceptance tests without needing development support of writing fixture specific code for each new transaction.

That became the main driving force behind the thought of developing a Generic Fixture where you just drop your class in FitNesse’s class path, write your test tables in wiki and be ready to test without having to write a single line of Java code. This idea is simple enough to understand but not so simple to implement because of the complexity of Java classes. Each class is different, it can have various type of constructors and methods (even overloaded ones). Each method can be returning different type of values (or not returning at all if method is void). We have to define some rules of defining class names, constructors, method names and their return types on wiki test tables first.

So here they are:

  1. Name of the class to test or system under test (SUT) and arguments to the SUT’s constructor must come in a separate cell of the very first row of the table. (Starting from 2nd cell, since 1st cell is reserved for name of the Fixture Class itself). eg:
    !| Generic Fixture | java.lang.String | It’s a cool fixture |
  2. Each row of the table will have a method name in the 1st cell followed by method parameters appearing each in a separate cell.
  3. Leave an empty cell after end of parameters and start of return values. eg:
    | indexOf | cool | | 7 | | concat | !! | | It’s a cool fixture!! |
  4. Class that is being tested can have methods accepting parameters of all primitive types (int, float, double etc) as well as of all the primitive Java wrapper classes (Integer, Double, String, Number Date etc).
  5. If methods need to accept user defined object in parameters then those user defined types must provide a method: public static Object parse(String str) { … }. An example of this is provided below in the Address class.
  6. Methods with return types of all primitive types (int, float, double etc) as well as of all the primitive Java wrapper classes (Integer, Double, String, Number etc) should be supported.
  7. Methods can also return array of all primitive types (int, float, double etc) as well as of all the primitive Java wrapper classes (Integer, Double, String, Number etc).
  8. Class that is being tested can be a non-instantiating class like java.lang.Math. In this case all methods will be invoked statically.
  9. Calls to static method must be allowed in the form of CanonicalClassName.MethodName eg:
    | java.lang.Thread.sleep | 1000 |
  10. Should be able to operate with overloaded methods in a class. For example abs() method in java.lang.Math class:
    | abs | -5.72 | | 5.72 |
  11. It should be able to store return value of a method into variables and a variable can be of any type. And later on this variable can be used anywhere in the test to pass a value, compare a return value, creating a new class or becoming a target itself. Please visit my other post Use of variables, arrays and complex types in Generic Fixture for more details.

Putting it all together: Lets write a simple test table for testing java.lang.String class. Lets say we have a Java code like this:

java.lang.String aString = new java.lang.String ( "It's a cool fixture" );
assertEquals ( aString.indexOf ( "cool"), 7 );
assertEquals ( aString.concat ( ", indeed" ), "It’s a cool fixture, indeed" );
assertEquals ( aString.compareTo ( "It’s a cool fixture" ), 0 );
assertEquals ( aString.equalsIgnoreCase ( "" ), false );
assertEquals ( aString.concat ( "" ), "It’s a cool fixture" );
assertEquals ( aString.equals ( "" ), false );
assertEquals ( aString.matches ( "^.*cool fix.*$" ), true );
System.out.println ( aString.toCharArray () );

And here is the FitNesse equivalent of above Java code using Generic Fixture:

!| Generic Fixture | java.lang.String | It's a cool fixture |
| indexOf | cool | | 7 |
| concat | , indeed | | It's a cool fixture, indeed |
| compareTo | It's a cool fixture | | 0 |
| equalsIgnoreCase | blank | | false |
| concat | blank | | It's a cool fixture |
| equals | blank | | false |
| matches | ^.*cool fix.*$ | | true |
| toCharArray | |

Address Test ResultsClick on Test button and you will get these results:
Assertions: 7 right, 0 wrong, 0 ignored, 0 exceptions

In this test we tested java.lang.String class and some of its methods. Notice last call in the test table above is String.toCharArray(), here we are not validating output of this call just getting output displayed. Nice thing is that we did not write any Java code for the String class we tested above. Looks pretty simple, isn’t it?

Let’s write another test table for java.lang.Math class where all the methods are declared as static:

!| Generic Fixture | java.lang.Math |
| sqrt | 25 | | 5.0 |
| round | 2.51 | | 3 |
| max | 3.469 | 3.47 | | 3.47 |
| java.lang.Thread.sleep | 1000 |
| abs | -5.72 | | 5.72 |

Click on Test button and you will get:

Assertions: 4 right, 0 wrong, 0 ignored, 0 exceptions

Again this Generic fixture was able to do the magic behind the scene and run all the static methods of java.lang.Math class. Did you notice I am calling | java.lang.Thread.sleep | 1000 | for introducing a delay of 1000 ms (1 sec) between max and abs method calls. This is the equivalent Java code for above fixture:

assertEquals ( Math.sqrt ( 25 ), 5.0 );
assertEquals ( Math.round ( 2.51 ), 3 );
assertEquals ( Math.max ( 3.469, 3.47 ), 3.47 );
java.lang.Thread.sleep ( 1000 );
assertEquals ( Math.abs ( -5.72 ), 5.72 );

So far we tested 2 Java classes without writing any code. You must be thinking why are we wasting our time testing standard core Java classes that everybody knows are working fine. How to write a meaningful acceptance test for a class of our own? So let’s take an example of a trivial Address class. Code is included here for reference:

Source Code Address.java

public class Address {
   public static class Name {
      private String surname = null;
      private String firstname = null;

      public Name() {
      }

      public Name(Name n) {
         this.surname = n.surname;
         this.firstname = n.firstname;
      }

      public static Object parse(String s) {
         Name n = new Name();
         String[] arr = s.split(" ");
         if (arr.length == 2) {
            n.setFirstname(arr[0]);
            n.setSurname(arr[1]);
         }
         return n;
      }

      @Override
      public String toString() {
         return this.firstname + " " + this.surname;
      }

      public String getSurname() {
         return surname;
      }

      public void setSurname(String surname) {
         this.surname = surname;
      }

      public String getFirstname() {
         return firstname;
      }

      public void setFirstname(String firstname) {
         this.firstname = firstname;
      }
   }

   private int streetNo = 0;
   private String street = null;
   private String city = null;
   private int postcode = 0;
   private String state = null;
   private Name name = null;

   public Address() {
      super();
   }

   public Address(Integer streetNo, String street, String city, int postcode,
     String state) {
      super();
      setAddress(streetNo, street, city, postcode, state);
   }

   public void setAddress(Integer streetNo, String street, String city,
     int postcode, String state) {
      this.streetNo = streetNo.intValue();
      this.street = street;
      this.city = city;
      this.postcode = postcode;
      this.state = state;
   }

   public void setStreetNo(Integer streetNo) {
      this.streetNo = streetNo;
   }

   public Integer getStreetNo() {
      return this.streetNo;
   }

   public String getStreet() {
      return this.street;
   }

   public void setCity(String city) {
      this.city = city;
   }

   public String getCity() {
      return this.city;
   }

   public void setPostcode(int postcode) {
      this.postcode = postcode;
   }

   public int getPostcode() {
      return this.postcode;
   }

   public String getFullAddress() {
      return this.streetNo + " " + this.street + ", " + this.city + ", "
        + this.state + " - " + this.postcode;
   }

   public void setStreet(String street) {
      this.street = street;
   }

   public String getState() {
      return this.state;
   }

   public void setState(String state) {
      this.state = state;
   }

   public Name getName() {
      return this.name;
   }

   public void setName(Name name) {
      this.name = name;
   }
}

Let’s assume I compiled this Address class and added it in FitNesse classpath. Now let’s go about writing an acceptance test table for this Address class using Generic Fixture.

!| Generic Fixture | Address | 123 | Nutley Street | New York | 21019 | NY |
| getStreetNo | | 123 |
| getCity | | New York |
| getState | | NY |
| getFullAddress | | 123 Nutley Street, New York, NY - 21019 |
| setStreetNo | 111 |
| setPostcode | 20133 |
| getPostcode | | 20133 |
| getStreetNo | | 111 |
| getState | | NY |
| setName | John Smith |
| getName | | John Smith |

Click on Test button and you will get:

Assertions: 8 right, 0 wrong, 0 ignored, 0 exceptions

Pay special attention to setName method in above test table. This method is taking a user defined object of Name type. Since I have implemented public static Object parse(String str) method in above code, generic Fixture is able to construct a Name object from the entered string “John Smith” in the table cell.

For the reference purpose above fixture is executing this Java code using Java reflection APIs:

Address addr = new Address(123, "Nutley Street", "New York", 21019, "NY" );
assertEquals ( addr.getStreetNo(), 123 );
assertEquals ( addr.getCity(), "New York" );
assertEquals ( addr.getState(), "NY" );
assertEquals ( addr.getFullAddress(), "123 Nutley Street, New York, NY - 21019" );
addr.setStreetNo ( 111 );
addr.setPostcode ( 20133 );
assertEquals ( addr.getPostcode (), 20133 );
assertEquals ( addr.getStreetNo(), 111 );
assertEquals ( addr.getState(), "NY" );
addr.setName ( "John Smith" );
assertEquals ( addr.getName(), "John Smith" );

That’s it, writing acceptance tests could be as simple as that. One more good thing about this Generic Fixture is that it eliminates the need of writing a separate Unit test class (eg: JUnit) for testing your business class.

Generic Fixture download and install instructions can be found on this page. And here is the complete source code for Generic Fixture for browsing. Please leave your comments, suggestions, bug reports, area of improvements and criticism for this effort.

Next Part: Use of variables, arrays and complex types in Generic Fixture

Coming up: Writing web application tests using this Generic Fixture In the next post I will demonstrate use of this Generic Fixture in writing some useful acceptance tests for Web applications.

Visit FitNesse Group Forum for a meaningful and thought provoking discussion on Generic Fixture.

About these ads

About anubhava

16 years of working experience in IT industry. Presently working as Lead Software Architect/Engineer with a Fortune 500 IT company in US. My LinkedIn Profile: http://www.linkedin.com/in/anubhavasrivastava
This entry was posted in fitnesse, generic fixture, install, java, selenium, tdd, test driven development and tagged , , , , , , , , , . Bookmark the permalink.

44 Responses to Introducing Generic Fixture for FitNesse

  1. anubhava says:

    Hi Hoon,

    I actually used fitcpp for testing my C/C++ applications. You can get it here: http://sourceforge.net/projects/fit/files/fit-cpp/ and follow this useful tutorial on using it: http://accu.org/index.php/journals/305

    Thanks, Anubhava

  2. Hoon says:

    Hi anubhava,

    It is great you’ve created a generic fixture. What I’m interested in is testing C/C++ applications using FitNesse and Fit/SLIM fixtures. You said you’ve tested C/C++ applications using Fit/FitNesse before creating a generic fixture in your article. Do you have a good tutorial or samples that show how to test C/C++ applications using FitNesse and Fit/SLIM fixtures? There is not much information on it on the Internet. Can you help?

  3. anubhava says:

    Hi Sonali,

    There are couple of ways you can repeat the tests. One easier is to create a test script and include it 5 times in a separate page using !include directive. Other way is to write a small script using Generic Fixture supported bean shell and execute your code test block as many times as you want.

    Hope that helps.
    cheers.

  4. Sonali says:

    Hi Anubhav,
    I want to loop on my fitnesse table how can i do that e.g
    |-script|giv wen zen for slim|
    |Given|I want to test|

    if want to execute I want to test 5 times is it achievable? and how?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s