Difference between revisions of "FitnesseSlim"
From ADempiere
This Wiki is read-only for reference purposes to avoid broken links.
(Created page with 'Wiki page with data to trigger setters and methods in ADempiereLogin.java - POC upgraded with Slim version of Fitnesse =Backgrou…') |
|||
(31 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: | + | *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 | + | **''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. | *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. | *Upon testing and debugging, i noted in my refactored code that the ADempiereInstance happens only once. This saves time for repeating rows to test. | ||
Line 55: | Line 92: | ||
} | } | ||
</pre> | </pre> | ||
− | *Furthermore the code (done by Carlos) | + | *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 68: | 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: 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
Contents
Background
- 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.
- The sourcecode for testing the following including the wiki page is online at:
- 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 FitnesseSlim (which is just the latest Fitnesse without any upgrading) and launch it via
java -jar fitnesse.jar -p 8080
- .. you are ready to go!
WikiText
- 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
- 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
- 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(); } }
- Please look up the SVN of http://sourceforge.net/projects/adempiere/ under /branches/FitnesseSlim for the example code here. 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