FitnesseSlim

From ADempiere
Jump to: navigation, search
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