ABL Unit Test case in a called .p

Posted by TrueDon on 27-May-2015 09:50

I've been trying to play with the ABL Unit and I've wondering if I'm using the ABL Unit correctly. 

I've opened a ABL Unit Type Project and have 3 .p's  in this simple project.

a.p - that calls b.p
b.p -  displays hello message  and  calls c.p
c.p - that contains a procedure(with message statement), the procedure call and a test case procedure.

My issue is that is when I attempt to run a.p in the Studio the option isn't there to run as a "Progress ABLUnit Application".   Also when I run a.p using ant or as an "Progress OpenEdge Application", the message is displayed from b.p and c.p but the test case in c.p isn't run.   Note, I can run c.p directly as "Progress ABLUnit Application" and the test case runs.  

Now if I add to a.p: 

@TestSuite(procedures="C:\Dev\MyTestProject\src\test\c.p").

the run as "Progress ABLUnit Application"  is available for a.p and only the procedure c.p is run along with the test case (simple ASSERT:Equals statement) which is as documented but if c.p takes in an input parameter then a runtime error is displayed in the results for the unit test.

How can I run a test case in c.p from a.p with all the logic in-between?
Are test cases suppose to be in .p's that do not have any input parameters when using the @TestSuite annotation?

Also is there any more documentation out there that anyone has discovered (besides chapter 6 of the Developer Studio manual)? 

All Replies

Posted by Ramadevi Dhavala on 27-May-2015 10:32

Hi,

The Run as "Progress ABLUnit Application" option won't be available for plain .p or .cls files. The files must at least contain a single test case with ABL annotations. Then only run as ABLUnit Application option will get enable. As the c.p contains, a test case procedure the option is shown for you. And as soon as you add @TestSuite(procedures="C:\Dev\MyTestProject\src\test\c.p") to a.p file, the option got enabled for you as you have added the ABLUnit test case (with ABLUnit annotation).

And as far as I know, the ABLUnit test cases won't support parameterized test cases. I am not sure of this.

Thanks,Rama.

[collapse]
From: TrueDon [mailto:bounce-TrueDon@community.progress.com]
Sent: Wednesday, May 27, 2015 8:21 PM
To: TU.OE.Development@community.progress.com
Subject: [Technical Users - OE Development] ABL Unit Test case in a called .p
 
Thread created by TrueDon

I've been trying to play with the ABL Unit and I've wondering if I'm using the ABL Unit correctly. 

I've opened a ABL Unit Type Project and have 3 .p's  in this simple project.

a.p - that calls b.p
b.p -  displays hello message  and  calls c.p
c.p - that contains a procedure(with message statement), the procedure call and a test case procedure.

My issue is that is when I attempt to run a.p in the Studio the option isn't there to run as a "Progress ABLUnit Application".   Also when I run a.p using ant or as an "Progress OpenEdge Application", the message is displayed from b.p and c.p but the test case in c.p isn't run.   Note, I can run c.p directly as "Progress ABLUnit Application" and the test case runs.  

Now if I add to a.p: 

@TestSuite(procedures="C:\Dev\MyTestProject\src\test\c.p").

the run as "Progress ABLUnit Application"  is available for a.p and only the procedure c.p is run along with the test case (simple ASSERT:Equals statement) which is as documented but if c.p takes in an input parameter then a runtime error is displayed in the results for the unit test.

How can I run a test case in c.p from a.p with all the logic in-between?
Are test cases suppose to be in .p's that do not have any input parameters when using the @TestSuite annotation?

Also is there any more documentation out there that anyone has discovered (besides chapter 6 of the Developer Studio manual)? 

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by TrueDon on 27-May-2015 11:34

Thanks Rama,

So from what I understand with my example:  a.p --calls--> b.p --calls--> c.p(ablunit @Test),  

There is no way to run the test in c.p from a.p without using the @TestSuite which will exclude any business logic in b.p.

In the example I mentioned, the test case wasn't parameterized the procedure c.p contained the input parameter. See Highlighted below.   The error is when we add @TestSuite(procedures="C:\Dev\MyTestProject\src\test\c.p") to a.p file and c.p has the input parameter below.  

Am I correct is assuming that in this case there is no way to run the test in c.p (below)? 

--------------------------------------------------------------------------------------

USING Progress.Lang.*.
USING OpenEdge.Core.Assert.
BLOCK-LEVEL ON ERROR UNDO, THROW.

DEFINE INPUT parameter p-char_in as character no-undo.
DEFINE VARIABLE vout AS CHARACTER NO-UNDO.

RUN stringcat (OUTPUT vout).

PROCEDURE stringcat:
  DEFINE OUTPUT PARAMETER vout       AS CHARACTER NO-UNDO.
  DEF    VAR      x           AS CHAR      NO-UNDO.
  DEF    VAR      vAcctExtXSD AS CHAR      NO-UNDO INITIAL "qwerty".

  x = 'AccountExtract ' + vAcctExtXSD.
  vout = x.
  DISPLAY  x VIEW-AS EDITOR SIZE 60 BY 40.    

END PROCEDURE.

@Test.
PROCEDURE teststringcat:
  ASSERT:Equals(vout,"dafasdsa").
END.    

Posted by Sanjeva Manchala on 27-May-2015 14:59

Hi,
 
When you run any ABL file as ABLUnit application, it only considers code inside life cycle methods(@Before, @After, @Setup, @TearDown) and Test methods(@Test). It won’t consider any other code. So, if you want to access input variable in your code then you have use that variable in any particular Test method or in Life cycle methods.
 
 
I have modified your code a little bit to run as ABLUnit application, Now you can run this as OpenEdge Application or ABLUnit Application:
BLOCK-LEVEL ON ERROR UNDO, THROW.
USING OpenEdge.Core.Assert FROM PROPATH.
@Setup.
PROCEDURE setUp:
    /*------------------------------------------------------------------------------
            Purpose:                                                                      
            Notes:                                                                       
    ------------------------------------------------------------------------------*/
    DEFINE INPUT PARAMETER p-char_in AS CHARACTER NO-UNDO.
END PROCEDURE
 
DEFINE VARIABLE vout AS CHARACTER NO-UNDO.
RUN stringcat (OUTPUT vout).
PROCEDURE stringcat:
    DEFINE OUTPUT PARAMETER vout       AS CHARACTER NO-UNDO.
    DEF VAR x           AS CHAR NO-UNDO.
    DEF VAR vAcctExtXSD AS CHAR NO-UNDO INITIAL "qwerty".
    x = 'AccountExtract ' + vAcctExtXSD.
    vout = x.
    DISPLAY  x VIEW-AS EDITOR SIZE 60 BY 40.   
END PROCEDURE.
 
@Test.
PROCEDURE teststringcat:
    DEFINE VARIABLE strcat AS CHARACTER NO-UNDO.
    RUN stringcat(OUTPUT strcat).
    ASSERT:Equals(strcat,"dafasdsa").
END.   
 
By the way, it’s always better to separate actual code and unit tests.
 
Hope this helps,
Sanjeev.
 
[collapse]
From: TrueDon [mailto:bounce-TrueDon@community.progress.com]
Sent: 27 May 2015 PM 10:05
To: TU.OE.Development@community.progress.com
Subject: RE: [Technical Users - OE Development] ABL Unit Test case in a called .p
 
Reply by TrueDon

Thanks Rama,

So from what I understand with my example:  a.p --calls--> b.p --calls--> c.p(ablunit @Test),  

There is no way to run the test in c.p from a.p without using the @TestSuite which will exclude any business logic in b.p.

In the example I mentioned, the test case wasn't parameterized the procedure c.p contained the input parameter. See Highlighted below.   The error is when we add @TestSuite(procedures="C:\Dev\MyTestProject\src\test\c.p") to a.p file and c.p has the input parameter below.  

Am I correct is assuming that in this case there is no way to run the test in c.p (below)? 

[/collapse]

Posted by TrueDon on 28-May-2015 13:12

Thanks for the reply Sanjeev,

I"m going to take your advice on seperating code from unit tests.  

I'm trying to see how I can use the ABL unit testing in our existing code.  In some cases we want to add a test case to an existing .p with an input parameter so it is the other way around where I want to keep the parameter in that procedure and be able to run the test case using the @TestSuite annotation so it doesn't cause the run-time error showing up in the results; because the ABLunit is looking at the input parameter that is outside the Life cycle methods you mentioned.  

Also following up on what you mentioned that the ABLUnit only considers code inside life cycle methods(@Before, @After, @Setup, @TearDown) and Test methods(@Test)  but in my example above the "RUN stringcat"  gets called outside of any Testing annotation when I run the ABL Unit, the code in the entire procedure gets run, some times multiple times.

If I run the below code I get the following messages, "xxx" "IN HERE" "xxx" "IN HERE" "setup" "xxx" "IN HERE" "setup".  

Should the two 'xxx' and any 'IN HERE' messages have been displayed since that code is outside any @ annotations?

USING OpenEdge.Core.Assert.

BLOCK-LEVEL ON ERROR UNDO, THROW.

DEFINE VARIABLE vout AS CHARACTER NO-UNDO.

DEFINE VARIABLE vhere AS CHARACTER NO-UNDO.

@Setup.

PROCEDURE Setup:

   MESSAGE "setup" VIEW-AS ALERT-BOX.

END.

RUN runproc(OUTPUT vout).

vhere = "IN HERE".

MESSAGE vhere VIEW-AS ALERT-BOX.

@Test.

PROCEDURE testcase1:

   ASSERT:Equals("aaa","bbb").  

END.

@Test.

PROCEDURE testcase2:

   ASSERT:Equals(vout,"xxx").  

END.

PROCEDURE runproc:

   DEFINE OUTPUT PARAMETER pout   AS CHARACTER NO-UNDO.

   pout = "xxx".    

   MESSAGE pout VIEW-AS ALERT-BOX.

END.

Posted by Matt Baker on 28-May-2015 13:57

 
 
 
I think you’ll find that writing unit tests is going to require a bit of framework in your environment.  Typically anything beyond a few simple tests is going to require some bootstrap framework that is convenience to access for developers and can be automated.   The framework is going to consist of some configuration elements along with some code to get that into your unit tests. 
 
Generally something like some continuous integration product like TeamCity or Jenkins that can pick up any new tests and run them automatically on a schedule is really useful…but not at all required.  But even if you are not using a CA tool you can make a few things consistent and reproducible without much work.  Consider wrapping your tests in an ant build.xml script, and you can call them directly from a CA tool or launch them directly from within PDSOE (PDSOE ships functionality to edit and run ant build.xml scripts), or you can run the test by itself from within PDSOE.  Keep the ant scripts with your unit tests as they are part of the code base. This way developers can find and execute them conveniently.
 
Part of that consistency step in your framework is abstracting any variable information like port numbers, machine names, database names and such out to configuration files where it can be generated by a script or by hand and easily adjusted for new environments.  Don’t pass varying information into the test via parameters.  Each test should test one thing.  Simple properties files that can be read in using a temp-table or a simple import statement and static variables that can be accessed directly your unit tests can make things convenient for adjusting them.
 
This is where “setup” and “teardown” come into play.  You want to keep your setup methods very simple so if something changes you don’t have to rework much. They are there for convenience of the test, but they are not part of the test itself.  Consider abstracting out things like loading property files, connecting to databases and appservers and such (unless those are the point of the test) to singular spots in your test code framework and not replicating that into each test.  Have a testing project as Sanjeev suggested in PDSOE that hosts the testing related framework code and the unit tests together lets you keep all this together.  Make this project build and run just like your project would in your scheduled builds.  If it is easy to run then you can get into the habit of running it to test code instead of running the debugger.
 
OO languages don’t allow have a “main block”.  But ABLUnit allows either .p or .cls based tests.  Try to keep any executable code (run statement ands o on) inside the annotated test, setup, or teardown methods.  This allows the framework more control over what gets executed and in what order.  With a unit test, behavior outside the scope of the testing method is “undefined”, you can avoid this by not using the main block for anything beyond variable definitions.  If you’re comfortable with OO code, write your unit tests using .cls files and you’ll avoid doing this by accident.
 
Sorry for the word wall L  Unit testing has been raised to an art form and there can be a huge number of things to consider.
 
mattB
 
 
[collapse]
From: TrueDon [mailto:bounce-TrueDon@community.progress.com]
Sent: Thursday, May 28, 2015 2:13 PM
To: TU.OE.Development@community.progress.com
Subject: RE: [Technical Users - OE Development] ABL Unit Test case in a called .p
 
Reply by TrueDon

Thanks for the reply Sanjeev,

I"m going to take your advice on seperating code from unit tests.  

I'm trying to see how I can use the ABL unit testing in our existing code.  In some cases we want to add a test case to an existing .p with an input parameter so it is the other way around where I want to keep the parameter in that procedure and be able to run the test case using the @TestSuite annotation so it doesn't cause the run-time error showing up in the results; because the ABLunit is looking at the input parameter that is outside the Life cycle methods you mentioned.  

Also following up on what you mentioned that the ABLUnit only considers code inside life cycle methods(@Before, @After, @Setup, @TearDown) and Test methods(@Test)  but in my example above the "RUN stringcat"  gets called outside of any Testing annotation when I run the ABL Unit, the code in the entire procedure gets run, some times multiple times.

If I run the below code I get the following messages, "xxx" "IN HERE" "xxx" "IN HERE" "setup" "xxx" "IN HERE" "setup".  

Should the two 'xxx' and any 'IN HERE' messages have been displayed since that code is outside any @ annotations?

USING OpenEdge.Core.Assert.

BLOCK-LEVEL ON ERROR UNDO, THROW.

DEFINE VARIABLE vout AS CHARACTER NO-UNDO.

DEFINE VARIABLE vhere AS CHARACTER NO-UNDO.

@Setup.

PROCEDURE Setup:

   MESSAGE "setup" VIEW-AS ALERT-BOX.

END.

RUN runproc(OUTPUT vout).

vhere = "IN HERE".

MESSAGE vhere VIEW-AS ALERT-BOX.

@Test.

PROCEDURE testcase1:

   ASSERT:Equals("aaa","bbb").  

END.

@Test.

PROCEDURE testcase2:

   ASSERT:Equals(vout,"xxx").  

END.

PROCEDURE runproc:

   DEFINE OUTPUT PARAMETER pout   AS CHARACTER NO-UNDO.

   pout = "xxx".    

   MESSAGE pout VIEW-AS ALERT-BOX.

END.

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by Jeff Ledbetter on 28-May-2015 14:27

Generally something like some continuous integration product like.. can pick up any new tests and run them automatically..”
 
Or, if you are Roundtable TSMS user, you can hook into the task-completion and object check-in hooks to automatically run your unit tests as well.
 
Jeff Ledbetter
skype: jeff.ledbetter

This thread is closed