Difference between revisions of "FitnesseSlim"

From ADempiere
Jump to: navigation, search
This Wiki is read-only for reference purposes to avoid broken links.
m (WikiText)
 
(24 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
{{Note|I am using [http://fitnesse.org/FitNesse.UserGuide.SliM Fitnesse Slim] to upgrade [[TestFramework]]. Expect more updates from time to time. I have also uploaded a real [http://sourceforge.net/projects/adempiere/files/Adempiere%20Packages/CostEngineTesting.pdf/download guide]. - [[User:Red1|Redhuan D. Oon]]}}
 +
 
[[Image:FitnesseLoginPage.gif|thumb|440px|right|Wiki page with data to trigger setters and methods in ADempiereLogin.java - POC upgraded with Slim version of Fitnesse]]
 
[[Image:FitnesseLoginPage.gif|thumb|440px|right|Wiki page with data to trigger setters and methods in ADempiereLogin.java - POC upgraded with Slim version of Fitnesse]]
 
=Background=
 
=Background=
*This page follows and stands on the good work from [[TestFramework]].
+
[[Image:FitnesseFromExcel.gif|thumb|right|450px|Any spreadsheet such as Excel can be the starting point to design your test data. Transfer to fitnesse is just copy/paste (See image below).]]
 +
*This page follows and stands on the good work from [[TestFramework]] based on [[Fitnesse]].
 
*Here i recall how i researched to do a [[Cost Engine/Testing]] framework.
 
*Here i recall how i researched to do a [[Cost Engine/Testing]] framework.
 
*The sourcecode for testing the following including the wiki page is online at:
 
*The sourcecode for testing the following including the wiki page is online at:
**http://adempiere.svn.sourceforge.net/viewvc/adempiere/FitnesseSlim/
+
**http://adempiere.svn.sourceforge.net/viewvc/adempiere/branches/FitnesseSlim/
 +
**Place the code anywhere in your ADempiere server and compile its bin for the !path statement later.
 
*Once you downloaded [http://www.fitnesse.org FitnesseSlim] (which is just the latest Fitnesse without any upgrading) and launch it via
 
*Once you downloaded [http://www.fitnesse.org FitnesseSlim] (which is just the latest Fitnesse without any upgrading) and launch it via
 
  java -jar fitnesse.jar -p 8080
 
  java -jar fitnesse.jar -p 8080
 
+
*.. you are ready to go!
  
 
=WikiText=
 
=WikiText=
 
+
[[Image:FitnesseExcel.gif|thumb|right|450px|You can also convert the test to Excel format and vice versa]]
 
*From the Frontpage at your  
 
*From the Frontpage at your  
 
  http://localhost:8080/  
 
  http://localhost:8080/  
 
*You can edit the FrontPage and create a sub-page i.e.  
 
*You can edit the FrontPage and create a sub-page i.e.  
 
  >LoginPage
 
  >LoginPage
*This will then lead to a new page for you to edit. You can put in following text (edit the paths to your local server occurences:
+
*This will then lead to a new page for you to edit. You can put in following text (edit the !paths to your local server occurrences):
 
<pre>
 
<pre>
 
!define TEST_SYSTEM {slim}
 
!define TEST_SYSTEM {slim}
Line 37: Line 41:
  
 
=POC for ADempiereLogin=
 
=POC for ADempiereLogin=
[[Image:FitnesseSlimLoginSuccess.gif|thumb|440px|right|A successful POC Login with easy user data to test true/false conditions. Just hit the ''Test'' button and get it intuitively on the same/single page.]]
+
[[Image:FitnesseSlimLoginSuccess.gif|thumb|440px|right|Purposely made wrong asserts to prove that the Test Engine really works.]]
 +
[[Image:FitnesseAsserts.gif|thumb|440px|right|A successful POC Login with easy user data to test true/false conditions. Just hit the ''Test'' button and get it intuitively on the same/single page.]]
 
*I reused CarlosRuiz's code and refactored them:
 
*I reused CarlosRuiz's code and refactored them:
 
**''r14692 /FitnesseSlim/src/fit/ (ADempiereLogin.java ModelLogin.java)'': Refactored ADempiereLogin as Fitnesse '''Fixture should not contain logic code''' but just Fixture data
 
**''r14692 /FitnesseSlim/src/fit/ (ADempiereLogin.java ModelLogin.java)'': Refactored ADempiereLogin as Fitnesse '''Fixture should not contain logic code''' but just Fixture data
 
**''r14693 /FitnesseSlim/src/fit/ (ADempiereLogin.java ModelLogin.java)'': '''Fixture should not produce output''', just true/false or assert types for Fitnesse final result.
 
**''r14693 /FitnesseSlim/src/fit/ (ADempiereLogin.java ModelLogin.java)'': '''Fixture should not produce output''', just true/false or assert types for Fitnesse final result.
 
**''r14694 /FitnesseSlim/src/fit/ADempiereLogin.java'': Fixture parse wiki input to be free from needing to put in @Ref tags, as '''column corresponds directly to Setter''' in java.
 
**''r14694 /FitnesseSlim/src/fit/ADempiereLogin.java'': Fixture parse wiki input to be free from needing to put in @Ref tags, as '''column corresponds directly to Setter''' in java.
*Test are easily setup and the results are easily read:
+
*Tests are easily setup and the results are easily read:
  
 
=Developer Notes=
 
=Developer Notes=
==WikiPage==
+
==Persistence Technique==
 +
*You can reuse instances by making context of objects persist while the test run go from one table to another or in reality from one java class to the other.
 +
*'''''Protected static''''' variables from '''''super class''''' are effective in this way.
 +
*This is needed as the test does not persist or commit to the Database it is testing against.
 +
*Please refer to the source link at [[Cost Engine/Case I]].
 +
 
 +
==WikiPage Format==
 +
*Wiki pages for Fitnesse are called StoryTests. They can comprise of Import, Path, Table or arrange in a Suite.
 
*The title label at the top of the table is used to look for the java class in your path bin.
 
*The title label at the top of the table is used to look for the java class in your path bin.
 
**i.e. ''ADempiere Login'' will look for '''ADempiereLogin.class'''. The words are automatically joined.
 
**i.e. ''ADempiere Login'' will look for '''ADempiereLogin.class'''. The words are automatically joined.
 +
*Fitnesse can work as a '''Suite''' where multiple classes with respective tables point to a single ''Suite Page'', with '''Teardown''' at the end of it.
 +
**Imports and Paths are then setup only once in the main suite page.
 
*The use of a simple multi-row decision table as above allows quick formulating of testing data and its handling in code.
 
*The use of a simple multi-row decision table as above allows quick formulating of testing data and its handling in code.
*Each column title acts as either a property setter (Client) or a method (ClientID?). This is highly intuitive to a java coder.
+
*Each column title acts as either a property setter (''Client'') or a method (''ClientID?''). This is highly intuitive to a java coder.
 
*An ordinary label such as 'Client' will look for its '''setClient(String Client)''' method.
 
*An ordinary label such as 'Client' will look for its '''setClient(String Client)''' method.
 
**As a setter allows me to obviously put any string parser at that setter method without asking again what that column type is.
 
**As a setter allows me to obviously put any string parser at that setter method without asking again what that column type is.
Line 58: Line 72:
 
**This way we can return values from the test easily and quickly.
 
**This way we can return values from the test easily and quickly.
 
*As seen above, paths are put in the wikipage content to ensure the ADempiere jars are within its classpath during execution.
 
*As seen above, paths are put in the wikipage content to ensure the ADempiere jars are within its classpath during execution.
*There is no need to follow (in my case) the geneJar.desc instruction from Sunny at [[TestFramework]], as just pathing to ADempiere jar is sufficient.
+
*I followed the geneJar.desc instruction from Sunny at [[TestFramework]], but I need not include ADempiereTrunk, as just making !path to ADHOME/lib/Adempiere.jar and ADHOME/lib/AdempiereCLib.jar is sufficient.
*Sometimes Fitnesse intermittently misbehaves where new pages cannot be created or Debugger not triggering the breaks. It takes all sorts of restarting or test pages to get a feel of it. I suspect that too many clumsy edits may cause issues but once resolved they mysteriously do not recur.
+
 
 +
===StoryTest Troubleshooting===
 +
*Ensure there are no spaces at the end of the whole line. I mean after the last '|'.
 +
*Ensure that the setter column is filled in with values. If a value is not used, just put in zero - 0.
 +
*Do not have double spacings in between columns. Sometimes it breaks the format.
 +
*You can easily check if formating is ok by just pressing 'save' of the edit and view if the table comes out completely.
  
 
==SourceCode==
 
==SourceCode==
Line 73: Line 92:
 
}
 
}
 
</pre>
 
</pre>
*Furthermore the code (done by Carlos) only checks for different login info before relogin again.
+
*Furthermore the code (done by Carlos) already checks for different login info before relogin again.
 
<pre>
 
<pre>
 
//Share login between different sessions
 
//Share login between different sessions
Line 86: Line 105:
 
return true; // already logged with same data
 
return true; // already logged with same data
 
</pre>
 
</pre>
 +
 +
==Look Up Case==
 +
[[Image:FitnesseLookUpCase.gif|thumb|left|400px|Versatility of Fitnesse to get objects with simple coding]]
 +
*Here we try to prove how versatile Fitnesse is.
 +
*It can traverse few objects on the same page. I.e. it can pass a variable return from one object as an argument of the other. See the appearances of '$I' below. It turns out to be '114' in the test run.
 +
*The example here extends the same text and 2 very simple extra classes.
 +
*The Fitnesse wiki text:
 +
<pre>
 +
|import|
 +
|fit|
 +
 +
!|ADempiere Login                                |
 +
|User    |Password|Client|ClientID?|Role|execute?|
 +
|GardenAdmin|GardenAdmin|GardenWorld|      |102 |true    |
 +
 +
!|Look up Order    |
 +
|Record ID | Business Partner Name? | BPartner ID? |
 +
|1000009 || $I= |
 +
 +
!|Get Details of | $I |
 +
|Name of Country?| 
 +
|  |
 +
</pre>
 +
*The 2 extra classes:
 +
<pre>
 +
package fit;
 +
 +
import org.compiere.util.Env;
 +
import org.compiere.model.*;
 +
 +
public class LookUpOrder {
 +
private int RecordID;
 +
MOrder order = null;
 +
 +
public void setRecordID(int recID) {
 +
this.RecordID = recID;
 +
}
 +
 +
public String BusinessPartnerName(){
 +
order = new MOrder(Env.getCtx(), RecordID, null);
 +
return order.getC_BPartner().getName();
 +
}
 +
 +
public int BPartnerID(){
 +
return order.getC_BPartner_ID();
 +
}
 +
 +
}
 +
</pre>
 +
<pre>
 +
package fit;
 +
 +
import org.compiere.model.MBPartnerLocation;
 +
import org.compiere.util.Env;
 +
 +
public class GetDetailsOf {
 +
private int bpartnerid;
 +
 +
public GetDetailsOf(int partnerID) {
 +
bpartnerid = partnerID;
 +
}
 +
 +
public String NameOfCountry(){
 +
MBPartnerLocation loc = new MBPartnerLocation(Env.getCtx(), bpartnerid, null);
 +
return loc.getLocation(true).getCountryName();
 +
}
 +
}
 +
</pre>
 +
 +
*Please look up the SVN of http://sourceforge.net/projects/adempiere/ under /branches/FitnesseSlim for the example code here. [http://adempiere.svn.sourceforge.net/viewvc/adempiere/branches/FitnesseSlim/ Browse].
 +
 +
==Deploying==
 +
*The classpath in the Fitnesse wiki page just points to:
 +
ADEMPIERE_HOME/lib/Adempiere.jar
 +
ADEMPIERE_HOME/lib/AdempiereCLib.jar
 +
*If you have customization or patches jars, you need not put in the classpaths. Just RUN_silentsetup will incorporate them into the AdempiereCLib.jar
 +
 
[[Category:Testing]]
 
[[Category:Testing]]

Latest revision as of 18:46, 4 February 2011

Note.gif Note:

I am using Fitnesse Slim to upgrade TestFramework. Expect more updates from time to time. I have also uploaded a real guide. - Redhuan D. Oon

Wiki page with data to trigger setters and methods in ADempiereLogin.java - POC upgraded with Slim version of Fitnesse

Background

Any spreadsheet such as Excel can be the starting point to design your test data. Transfer to fitnesse is just copy/paste (See image below).
java -jar fitnesse.jar -p 8080
  • .. you are ready to go!

WikiText

You can also convert the test to Excel format and vice versa
  • From the Frontpage at your
http://localhost:8080/ 
  • You can edit the FrontPage and create a sub-page i.e.
>LoginPage
  • This will then lead to a new page for you to edit. You can put in following text (edit the !paths to your local server occurrences):
!define TEST_SYSTEM {slim}
!path /Users/red1/Documents/workspace/FitnesseSlim/bin
!path /Users/red1/Documents/projects/Fitnesse/fitnesse.jar
!path /Applications/Adempiere/lib/Adempiere.jar
!path /Applications/Adempiere/lib/AdempiereCLib.jar

|import|
|fit|

!|ADempiere Login                                 |
|User     |Password|Client|ClientID?|Role|execute?|
|SuperUser|System  |System|$p=      |0   |true    |
|SuperUser|System|GardenWorld|$p=      |102 |true    |
|GardenAdmin|GardenAdmin|GardenWorld|$p=      |0 |true    |
|GardenAdmin|GardenAdmin|GardenWorld|$p=      |102 |true    |
|SuperUser|System|GardenWorld|$p=      |0 |true    |
  • On saving the page should look as shown at the top.

POC for ADempiereLogin

Purposely made wrong asserts to prove that the Test Engine really works.
A successful POC Login with easy user data to test true/false conditions. Just hit the Test button and get it intuitively on the same/single page.
  • I reused CarlosRuiz's code and refactored them:
    • r14692 /FitnesseSlim/src/fit/ (ADempiereLogin.java ModelLogin.java): Refactored ADempiereLogin as Fitnesse Fixture should not contain logic code but just Fixture data
    • r14693 /FitnesseSlim/src/fit/ (ADempiereLogin.java ModelLogin.java): Fixture should not produce output, just true/false or assert types for Fitnesse final result.
    • r14694 /FitnesseSlim/src/fit/ADempiereLogin.java: Fixture parse wiki input to be free from needing to put in @Ref tags, as column corresponds directly to Setter in java.
  • Tests are easily setup and the results are easily read:

Developer Notes

Persistence Technique

  • You can reuse instances by making context of objects persist while the test run go from one table to another or in reality from one java class to the other.
  • Protected static variables from super class are effective in this way.
  • This is needed as the test does not persist or commit to the Database it is testing against.
  • Please refer to the source link at Cost Engine/Case I.

WikiPage Format

  • Wiki pages for Fitnesse are called StoryTests. They can comprise of Import, Path, Table or arrange in a Suite.
  • The title label at the top of the table is used to look for the java class in your path bin.
    • i.e. ADempiere Login will look for ADempiereLogin.class. The words are automatically joined.
  • Fitnesse can work as a Suite where multiple classes with respective tables point to a single Suite Page, with Teardown at the end of it.
    • Imports and Paths are then setup only once in the main suite page.
  • The use of a simple multi-row decision table as above allows quick formulating of testing data and its handling in code.
  • Each column title acts as either a property setter (Client) or a method (ClientID?). This is highly intuitive to a java coder.
  • An ordinary label such as 'Client' will look for its setClient(String Client) method.
    • As a setter allows me to obviously put any string parser at that setter method without asking again what that column type is.
    • Each column will have its own setter method.
  • To do something but not as a setter, then use a ?.
    • By putting a question mark after a label, it will look for a method with the same word. I.e. 'ClientID?' will look for ClientID() method.
    • It will post its return in the field space if a qualifier is present i.e. $P=.
    • This way we can return values from the test easily and quickly.
  • As seen above, paths are put in the wikipage content to ensure the ADempiere jars are within its classpath during execution.
  • I followed the geneJar.desc instruction from Sunny at TestFramework, but I need not include ADempiereTrunk, as just making !path to ADHOME/lib/Adempiere.jar and ADHOME/lib/AdempiereCLib.jar is sufficient.

StoryTest Troubleshooting

  • Ensure there are no spaces at the end of the whole line. I mean after the last '|'.
  • Ensure that the setter column is filled in with values. If a value is not used, just put in zero - 0.
  • Do not have double spacings in between columns. Sometimes it breaks the format.
  • You can easily check if formating is ok by just pressing 'save' of the edit and view if the table comes out completely.

SourceCode

  • A big thanks to CarlosRuiz for the bulk of the gruntwork in making a proper testing code.
  • The tests were trying to see which login info goes with which Role ID.
  • Upon testing and debugging, i noted in my refactored code that the ADempiereInstance happens only once. This saves time for repeating rows to test.
    • I ensured this by refactoring it into a superclass constructor:
	public ModelLogin() {
		if (adempiereInstance == null) {
			adempiereInstance = StaticAdempiereInstance.getInstance();
		}
	}
  • Furthermore the code (done by Carlos) already checks for different login info before relogin again.
		//Share login between different sessions
		if (   m_ads != null
			&& m_ads.isLoggedIn()
			&& m_ads.getM_AD_Client_ID() == m_client_id
			&& m_ads.getM_AD_Org_ID() == m_org_id
			&& m_ads.getAD_Role_ID() == m_role_id
			&& m_ads.getM_Warehouse_ID() == m_warehouse_id
			&& m_ads.getUser().equals(m_user)
			)
			return true; // already logged with same data

Look Up Case

Versatility of Fitnesse to get objects with simple coding
  • Here we try to prove how versatile Fitnesse is.
  • It can traverse few objects on the same page. I.e. it can pass a variable return from one object as an argument of the other. See the appearances of '$I' below. It turns out to be '114' in the test run.
  • The example here extends the same text and 2 very simple extra classes.
  • The Fitnesse wiki text:
|import|
|fit|

!|ADempiere Login                                 |
|User     |Password|Client|ClientID?|Role|execute?|
|GardenAdmin|GardenAdmin|GardenWorld|      |102 |true    |

!|Look up Order     |
|Record ID | Business Partner Name? | BPartner ID? |
|1000009 || $I= |

!|Get Details of | $I | 
|Name of Country?|  
|  |
  • The 2 extra classes:
package fit;

import org.compiere.util.Env;
import org.compiere.model.*;

public class LookUpOrder {
	private int RecordID;
	MOrder order = null;

	public void setRecordID(int recID) {
		this.RecordID = recID;
	}
	
	public String BusinessPartnerName(){
		order = new MOrder(Env.getCtx(), RecordID, null);
		return order.getC_BPartner().getName();
	}
 
	public int BPartnerID(){	
		return order.getC_BPartner_ID();
	}

}
package fit;

import org.compiere.model.MBPartnerLocation;
import org.compiere.util.Env;

public class GetDetailsOf {
	private int bpartnerid;
	
	public GetDetailsOf(int partnerID) {
		bpartnerid = partnerID;
	}

	public String NameOfCountry(){
		MBPartnerLocation loc = new MBPartnerLocation(Env.getCtx(), bpartnerid, null);
		return loc.getLocation(true).getCountryName();
	}
}

Deploying

  • The classpath in the Fitnesse wiki page just points to:
ADEMPIERE_HOME/lib/Adempiere.jar
ADEMPIERE_HOME/lib/AdempiereCLib.jar
  • If you have customization or patches jars, you need not put in the classpaths. Just RUN_silentsetup will incorporate them into the AdempiereCLib.jar