Powered By Blogger

Friday, January 31, 2014

Multipart Fitnesse fixture for Restful Apis

Fitnesse Fixture to handle Multipart/mixed Requests



Just wrote a fitnesse fixture for multipart mixed content types.
The already existing smartrics rest fixtures can handle only multipart/form-data not multipart/mixed.

It is also strange that the Httpcomponent 3.2  does not support multipart/mixed, but only multipart/form-data. Worse enough, I see no documentation that it does not support multipart/mixed. Going through the source code, I could see that the 'multipart/form-data' content type is hard-coded all over the place.

My fixture is written using :
Http component 4.3 : I have added additional support for multipart/mixed, apart from the already existing multipart/form-data
Smartrics rest fixutre 3.0 : Extended the already existing restfixture class and the partfactory class to support multipart/mixed

See my Google code Repository

Sample request to handle Multipart Mixed :


!|smartrics.rest.fitnesse.fixture.RestFixtureConfig|
|http.client.connection.timeout|55000|
|restfixture.display.actual.on.right|true|
|restfixture.default.headers|defHeader : 1|
|restfixture.xml.namespace.context|!-xsalias=${namespace1}
ysalias=${namespace2}
-!|
|restfixture.content.handlers.map|application/xml=XML|
!|com.goraksh.fitnesse.fixture.FitMultipartRestFixture|http://${BASE_URI}|
|setBody|....|
|uploadFile|D:\testcase\hello.txt|
|setHeader|X-session-header : %sessionId%|
|POST|/${RESOURCE_PATH}?${QUERY_PARAMS}|201|!-Cache-Control : no-cache
pragma : no-cache
Content-Type : application/xml-!||

Sample Request to handle Multipart/form-data


!|smartrics.rest.fitnesse.fixture.RestFixtureConfig|
|http.client.connection.timeout|55000|
|restfixture.display.actual.on.right|true|
|restfixture.default.headers|defHeader : 1|
|restfixture.xml.namespace.context|!-xsalias=${namespace1}
ysalias=${namespace2}
-!|
|restfixture.content.handlers.map|application/x-www-form-urlencoded=FORM-DATA|
!|com.goraksh.fitnesse.fixture.FitMultipartRestFixture|http://${BASE_URI}|
|setBody|....
|uploadFile|D:\testcase\hello.txt|
|setHeader|X-session-header : %sessionId%|
|POST|/${RESOURCE_PATH}?${QUERY_PARAMS}|201|!-Cache-Control : no-cache
pragma : no-cache
Content-Type : application/xml-!||

Thursday, January 23, 2014

Fitnesse Generic Fixture


Fitnesse Generic Fixture:


1. Use genericfixture.jar in the classpath, at the beginning of the script as:

classpath: C:\Fitnesse\RestFixture-2.0.beta.2_29th NOV_II\lib\genericfixture.jar

2. Write the Java code for Fixture. It may or may not extend fit.Fixture class
Create  a jar and used in the script classpath as:
classpath: D:\MyFit\myfit.jar

Here name of the jar is myfit.jar that contains the required code for Generic Fixtures

Like AdderTest: class.


It has fw public methods:
a.) getUrl ==> This method is a setter method.
b.) add(a,b) ==> This method takes two arguments and returns the sum
c.) getId() ==> This method does not take any arguemnt. It parses the Url and the returns a substring, the trailing ID.

Now how to use this class as generic fixture:

A fitnesse script :

 Do a Http get

!|smartrics.rest.fitnesse.fixture.RestFixtureConfig|
|restfixture.xml.namespace.context|!-xsalias=http://bindings.egain.com/ws/model/v12/gen/kb
ysalias=http://bindings.egain.com/ws/model/v12/gen/common
zsalias=http://bindings.egain.com/ws/model/v12/gen/platform
-!|
!|smartrics.rest.fitnesse.fixture.RestFixture|http://${mname}/system/ws/v12/kb|
|setHeader|X-egain-session : %agent1sessionId%|
|GET|/attachment?article=${articleId}&version=${versionId}&$attribute=all|200|Content-Type:application/xml||
|let| contentUrl |body|/xsalias:Attachments/xsalias:attachment/xsalias:contentUrl[text()]||
|let | a | body |/xsalias:Attachments/xsalias:attachment/xsalias:type[text()]| |

Use of Generic fixture

!| Generic Fixture | myfit.AdderTest |
| getUrl | contentUrl  |
| var=getId|   | 0b5cd3c5-c070-4bfc-a748-920aa5e0feb7  |


Use of variable 'var'

!|smartrics.rest.fitnesse.fixture.RestFixtureConfig|
|restfixture.xml.namespace.context|!-xsalias=http://bindings.egain.com/ws/model/v12/gen/kb
ysalias=http://bindings.egain.com/ws/model/v12/gen/common
zsalias=http://bindings.egain.com/ws/model/v12/gen/platform
-!|
!|smartrics.rest.fitnesse.fixture.RestFixture|http://${mname}/system/ws/v11/internal|
|setHeader|X-egain-session : %agent1sessionId%|
|GET|/stream/%var%|200|Content-Type:text/plain||

Explanation


Generic Fixture must start with the name: Generic Fixture. Use ! mark to start.
Second column is the name of the Class( AdderTest )
This class does not have a constructor. Incase it had one, there should be a third column like:
!| Generic Fixture | myfit.AdderTest | constructorArg1 | constructorArgs2 |

Now the second row, third row start wth method name:

Second Row: Here getUrl is the name of the method. Second column is the value passed to the method getUrl.
This value must match the type of the method argument type. Also this value is passed statically.i.e the value accessible in the getUrl method is actually "contentUrl", not the one extracted by the RextFixture.

To reflect the dynamic value in the getUrl method, it needs to be processed like:

String newVal = "";
   if (Fixture.hasSymbol(val))
        newVal = Fixture.getSymbol(val).toString();
return newVal;

There is no third column for second row. The column has to be left blank to denote that the method returns at this point. Next column after this blank will match the return string.

Like 
| getUrl | contentUrl  |   |    /system/ws/v12/internal/stream/0b5cd3c5-c070-4bfc-a748-920aa5e0feb7 |

Here third column is blank because it denotes a return from method. Next column is the value to be matches. So this test case expects the value returned from getUrl method to be "/system/ws/v12/internal/stream/0b5cd3c5-c070-4bfc-a748-920aa5e0feb7"

It's like : assert getUrl() equals "/system/ws/v12/internal/stream/0b5cd3c5-c070-4bfc-a748-920aa5e0feb7"

Third Row: 
Here again  the first column is the method name. It intends to call getId method. But this testcase also intends to assign the value returned by getId to a variable so that it can be used in further test cases. Hence var=getId. Value is stored in variable 'var'. 
Column two is blank, which denotes a return from method getId. Column three is an assertion i.e this test case expects '0b5cd3c5-c070-4bfc-a748-920aa5e0feb7 ' to be returned from the getId method.

Code


package myfit;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;

import fit.Fixture;

public class AdderTest extends fit.Fixture {
private String url ;
public AdderTest (  ) {
        String sampleUrl = "/system/ws/v12/internal/stream/0b5cd3c5-c070-4bfc-a748-920aa5e0feb7";
System.out.println( s.substring( s.indexOf("eam/") + 4 )); // prints 0b5cd3c5-c070-4bfc-a748-920aa5e0feb7
}
public void getUrl( String ss ) {
this.url = getStringVal(ss );
}
public String getStringVal(String val){
String newVal = "";
  if (Fixture.hasSymbol(val))
       newVal = Fixture.getSymbol(val).toString();
return newVal;
}

public int add( int a , int b ) {
return a+b;
}
public String getId() {
if ( url == null ) return "null";
if ( url.indexOf("eam/") == -1  ) return "-1";
return url.substring( url.indexOf("eam/") + 4 );
}
}

Command line to compile , run and create jar:

javac -classpath lib\fitnesse.jar  src\myfit\*.java -d .\bin
@REM java -cp D:\MyFit\bin fit.Adder

cd bin
jar cvf D:\MyFit\myfit.jar myfit/*

@REM  java -classpath lib\fitnesse.jar;myfit.jar myfit.AdderTest

Now myfit.jar can be used in the import statements of the script


Example # 1

Use java.lang.sleep to cause a delay between tests:

!| Generic Fixture |
| java.lang.Thread.sleep | 1000 |