• No results found

ABLUnit Testing Framework

N/A
N/A
Protected

Academic year: 2021

Share "ABLUnit Testing Framework"

Copied!
14
0
0

Loading.... (view fulltext now)

Full text

(1)
(2)

Page 2

Table of Contents

Overview ... 3

Introduction to ABLUnit ... 3

Where and when should we consider writing unit tests? ... 3

Terminology ... 4

Unit test lifecycle ... 4

Setting up the environment for writing/running ABLUnit tests ... 5

Writing your first Unit test ... 6

Running unit tests and viewing results ... 11

Preparing the suite ... 12

Integrating with build system ... 12

Debugging unit tests ... 13

Platforms/Version support ... 14

Working with ABLUnit in Linux/Unix environment ... 14

Working with ABLUnit in pre-11.4 versions ... 14

(3)

Page 3

Overview

The purpose of this document is to provide an overview of the ABLUnit testing framework and guide users on how to write end to end ABLUnit tests by walking through a simple example.

Introduction to ABLUnit

Unit tests are the tests written to verify a small unit of functionality in a software system. Unit testing provides the ability, for any set of inputs, to determine if the software is returning proper values and will gracefully handle failures during the course of execution. Unit tests are written and executed by developers to make sure that code meets its design and requirements and behaves as expected.

ABLUnit Testing Framework is a unit testing framework from Progress OpenEdge for testing software written in ABL. This framework is available for customers starting OpenEdge 11.4 and has support for both Classes and Procedures.

Where and when should we consider writing unit tests?

We hear this question frequently. There is a clear distinction between Unit tests and UI tests. UI Tests are not same as unit tests. UI Tests attempt to subjectively verify that elements on the screen behave as expected when a user performs a given action. There is no math or logic involved.

Unit tests are generally not suitable for testing complex UI interaction events with the system. Unit tests verify logic. A unit test generally tests the smallest possible unit of code (which could be a method, a procedure, a class, or component), without dependencies on system or network resources.

In this manner, unit tests are essentially mathematical proofs that confirm what our business logic is supposed to do.

Writing unit tests for an existing or old code is usually hard because the code was not designed to be tested. So, the starting point would be creating a set of high level end-to-end tests that we can run to validate core application functionalities. Also we need to make sure that any new code that gets added has appropriate unit tests.

Few people want to write unit tests as part of their source code. Issue with this approach is who tests the logic in the tests. The issue being if our test logic and our source code (business logic) are both broke, we may miss an actual failed test. Also, by writing unit tests separately from source code, it makes easier to run the test cases.

(4)

Page 4

Unit tests can also be shared with other developers, implying lifecycle methods or procedures to be re-used for common features.

Terminology

Following are the terms we need to know in ABLUnit Testing framework.  Test Case

 Test  Assertions  Test Suite Test Case

A Test Case refers to an ABL procedure file or an OOABL class file. Test Case contains multiple tests.

Test

A Test is an internal procedure in a procedure file or a method in a class file which is annotated with @Test annotation. From here on wards, whenever I refer to a test, it can be either test procedure in a .p file or test method in a class file.

Assertions

Assertions are used to determine whether a test is passed or failed. Assertions are central to unit testing in any Unit testing framework and ABLUnit is no exception for this. ABLUnit provides assertion methods for all ABL data types. The success or failure of these assertions is tracked by the framework, and can be viewed when a test run is complete.

Test Suite

A test suite is collection of Test Cases, whereas a Test Case contains individual test methods. A Test Suite is used to organize test cases into a logical group and run them as a group. Test Suite can be any ABL class or procedure. A test suite can include any number of test cases or other test suites and run them together as a group.

Unit test lifecycle

Following image shows the lifecycle of ABLUnit’s execution.

In ABLUnit, every test should be annotated with @Test annotation. A Test case procedure or a Test case class can have any number of tests and one set of Setup and TearDown methods/procedures which are annotated with @Setup and @TearDown annotations. A test case procedure or class can have one Before and After methods/procedures which are annotated with @Before and @After methods. You can find detailed explanation of lifecycle in Product documentation.

(5)

Page 5

Setup and TearDown method/procedure if exists, is called before executing each test. As mentioned before, we can have one set of setup and teardown lifecycle methods/procedures in a test case procedure/class so that the same set of setup and teardown will be executed for each Test. Before and After methods/procedures if exists are called once for Test Class or Procedure. At this point, do not worry if this is not entirely clear. We will follow with an example which will make this more clear.

Setting up the environment for writing/running ABLUnit tests

Following are the procedure libraries required to work with ABLUnit. We need to add these libraries to PROPATH.

OpenEdge.Core.pl - Contains Assertions required to work with ablunit.

ablunit.pl – Core framework containing a runner (ablunitcore.p) which is responsible for running unit test cases.

Out of these two libraries, OpenEdge.Core.pl file already exists in PROPATH and ablunit.pl will be available under gui and tty folders in <DLC>.

If you are using PDS OE as development environment, PDS OE provides a new perspective for working with ABLUnit called OpenEdge ABLUnit, where it gives a layout with all views required to work with ABLUnit feature in PDS OE.

We can add a facet called ABLUnit to existing project or create a new project of type ABLUnit. This will add all the required libraries that we discussed just now both OpenEdge.Core.pl and ablunit.pl to project’s PROPATH and also creates a new folder, tests as a placeholder to organize all unit tests. Below is the simple project structure.

(6)

Page 6

src -> Placeholder for source code

tests -> Placeholder for test code (ablunit tests) rcode -> Placeholder for generated rcode

PDS OE also provides tooling for writing unit tests through wizards. It will take care of generating test structure for us.

Writing your first Unit test

Let’s take the example of a login page written in ABL and write unit tests for the same. Since unit tests are not intended to test UI, we need to test the business logic behind the login page. Following is the login page UI.

Following is the code snippet say business logic for Login page. The purpose of the code snippet is to validate user login details against the user details in database table ‘Login’.

(7)

Page 7

/*--- File : main.p

Created : Mon Jun 15 15:00:50 EDT 2015 Notes :

---*/ USING Progress.Lang.AppError FROM PROPATH.

DEFINE VARIABLE mye AS Progress.Lang.AppError NO-UNDO. PROCEDURE loginProc :

DEFINE INPUT PARAMETER username AS CHARACTER. DEFINE INPUT PARAMETER password AS CHARACTER.

DEFINE OUTPUT PARAMETER loginResult AS CHARACTER INIT "Fail".

IF username EQ ? THEN DO:

mye = NEW Progress.Lang.AppError("Username is empty", 200). mye:ReturnValue = "Username cannot be empty".

RETURN ERROR mye. END.

FOR EACH Login WHERE Login.username = username: IF Login.password = password THEN

loginResult = "Success". END.

END PROCEDURE.

Now, let’s write unit tests for the above code snippet, loginProc. To validate the user login, we will consider the following cases.

1. Test valid login credentials 2. Test invalid login credentials 3. Test for null credentials

In order to run the above procedure from our tests, we need to: a. Connect to database

b. Run main procedure using handle in persistent mode c. Provide input values

d. Run internal procedure loginProc with input and output parameters e. Validate the resultant output parameter with expected result f. Delete any object handles.

(8)

Page 8

Let us break these steps. Since steps a,b and f,g are same for each test, we need to separate them from actual test code in order to avoid repetitions. Normally we would prefer to open a DB connection, run all DB tests and then close the connection but it sometimes depends on your requirements and might have to do this for each test separately. We will consider a normal case where we need to open a connection during start of tests and then close it after executing all the tests.

Now if we look at the picture above, Before and After are exactly for this purpose. Having the code to connect to database in Before method will make sure that this is executed only once at the start before running tests and before running Setup method/procedure.

So, here are the lifecycle procedures we can add.

/* This lifecycle procedure will run before running the actual tests */

@Before.

PROCEDURE Before:

CONNECT C:\qa\testdb\ablunit.db -H localhost -S 5522 NO-ERROR. END PROCEDURE.

/* This lifecycle procedure will run after running all tests */ @After.

PROCEDURE tearDownAfterProcedure: DISCONNECT ablunit.

DELETE PROCEDURE THIS-PROCEDURE.

END PROCEDURE.

The above two lifecycle procedures will take care of connecting the AVM to database before running all the tests and disconnecting the database after all tests are executed. In other words, the parameters that we set to AVM can be added to these Before and After tests. After database is connected, we need to run the source file (main.p) in persistent mode using handle before running the actual internal procedure where logic resides. Along with that, handle needs to be deleted after its usage.

Here in this example, each and every test scenario (test with valid details, test with invalid details, and a test with empty details) needs main.p to be executed in persistent mode. Instead of adding these lines of code repeatedly for each and every test, we can add them to Setup and TearDown lifecycle procedures.

(9)

Page 9

@Setup.

PROCEDURE setUp:

RUN main.p PERSISTENT SET prochandle. END PROCEDURE.

@TearDown.

PROCEDURE tearDown:

DELETE PROCEDURE procHandle. END PROCEDURE.

For each test, Setup procedure will be run before executing the test and TearDown procedure will run after executing the test.

Note: Writing lifecycle procedures, (Setup and TearDown) is optional for any test case. However, separating repeated operations to methods or procedures will create an understandable test lifecycle.

Now, individual tests can take care of preparing different inputs, running the actual logic with that input and verifying the result value with expected value using Assertions. Assertion API throws AssertionFailedError() when a test is failed. So, we need to add block-level or routine-block-level directive in our test code in order to get proper results.

(10)

Page 10

USING OpenEdge.Core.Assert FROM PROPATH.

BLOCK-LEVEL ON ERROR UNDO, THROW.

DEFINE VARIABLE procHandle AS HANDLE NO-UNDO. DEFINE VARIABLE uname AS CHARACTER NO-UNDO. DEFINE VARIABLE pwd AS CHARACTER NO-UNDO. DEFINE VARIABLE res AS CHARACTER NO-UNDO. @Before.

PROCEDURE Before:

CONNECT C:\qa\testdb\ablunit.db -H localhost -S 5522 NO-ERROR. END PROCEDURE.

@Setup.

PROCEDURE setUp:

RUN main.p PERSISTENT SET prochandle. END PROCEDURE.

@Test.

PROCEDURE validusr: /* Test1 */ uname = "test".

pwd = "test".

RUN loginProc IN procHandle (INPUT uname,INPUT pwd,OUTPUT res) . Assert:equals("success", res).

END PROCEDURE. @Test.

PROCEDURE invalidusr1: /* Test2 */ uname = "ablunit".

pwd = "test".

RUN loginProc IN procHandle (INPUT uname,INPUT pwd,OUTPUT res) . Assert:equals("success", res).

END PROCEDURE. @Test.

PROCEDURE emptyUsr: /* Test3 */ uname = ?.

pwd = "invalid".

RUN loginProc IN procHandle (INPUT uname,INPUT pwd,OUTPUT res) . Assert:equals("success", res).

END PROCEDURE. @TearDown.

PROCEDURE tearDown:

DELETE PROCEDURE procHandle. END PROCEDURE.

@After.

PROCEDURE tearDownAfterProcedure: DISCONNECT ablunit.

DELETE PROCEDURE THIS-PROCEDURE. END PROCEDURE.

(11)

Page 11

In the above example, before running the actual tests, internal procedure with Before annotation will be executed which connects the AVM to the Database. This before procedure prepares the environment ready for tests to execute.

After Before procedure, Setup procedure will be executed which will take care of running the main.p in persistent mode. And now, the actual test gets executed followed by the clean-up activities for that test by running a procedure with @TearDown annotation. This cycle say both Setup and TearDown procedures will run for each and every test procedure or test method in test case class or test case procedure.

Finally, after the execution of all tests, environment can be cleaned up by running procedure with After annotation like disconnecting database and deleting objects.

Running unit tests and viewing results

Once writing unit tests are completed, they can be tested by running individual tests either from PDS OE by using Run Configurations or we can run tests from command-line. The details on running tests can be found in product documentation.

Once the tests are executed, we can view results using ABLUnit view in PDS OE. We can rerun a particular test method/procedure, test class, test procedure or a test suite from this view. If tests are executed in command line, we can import the result file to view the result in Results view.

(12)

Page 12

Preparing the suite

Once we have multiple test cases, we may need them to organize test cases related to same functionality as a group and run them together. Test Suite serves this purpose. All the test cases in suite will be executed in the order we added. TestSuite can be either a procedure or a class file and it can include both test classes and test procedures. Test Suite is an empty ABL class or procedure with annotation @TestSuite containing the list of test cases. All the test case procedures are grouped with parameter name, procedures and test case classes with parameter name, classes.

BLOCK-LEVEL ON ERROR UNDO, THROW.

@TestSuite(procedures="UserValidation.p,Login.p"). @TestSuite(classes="BasicOp.cls").

Integrating with build system

Unit tests can be run as part of Continuous Integration process or as part of nightly builds. This can be done either by running unit tests from command-line or including them as part of build using Ant.

Running from command-line

Following is the command to run ABLUnit tests from command-line.

prowin -p ABLUnitCore.p -param "TestCaseClass.cls -outputLocation <path>"

Running from Ant

OpenEdge provides Ant task for running ablunit tests as part of automated builds. OpenEdge provides ant-ablunit.jar file (<DLC>\Java) to work with Ant. Following is the sample build.xml file.

(13)

Page 13

<project name="OE" default="ablunit" basedir=".."> <property environment="env" />

<property name="DLC_HOME" value="C:\Progress\OpenEdge"/> <!-- Target for defining 'taskdef' -->

<target name="taskdef">

<taskdef name="ablunit"

classname="com.progress.openedge.ant.ablunit.ABLUnitTask" classpath="${DLC_HOME}\java\ant-ablunit.jar" /> </target>

<!-- Target for defining 'ablunit' -->

<target name="ablunit" depends="taskdef" description="runs unit tests">

<mkdir dir="Results"/>

<ablunit dlc="${DLC_HOME}" environment="gui" printsummary="true" tempdir="${wrkdir}">

<batchtest todir="Results">

<fileset dir="tests" includes="**/*.p" /> <fileset dir="tests" includes="**/*.cls" /> </batchtest>

<dbinfo name="C:\qa\testdb\ablunit.db" host="localhost" port="5522"/>

<propath>

<pathelement path="src" /> <pathelement path="tests" />

<pathelement location="${DLC_HOME}\gui\ablunit.pl" />

<pathelement location="${DLC_HOME}\gui\OpenEdge.Core.pl" /> </propath>

</ablunit> </target>

</project>

Debugging unit tests

Though Progress Developer Studio for OpenEdge provides support only for running ABLUnit tests, Tests can be debugged by writing a procedure or a class file which executes the test method or test procedure.

(14)

Page 14

Platforms/Version support

Working with ABLUnit in Linux/Unix environment

ABLUnit feature comes with Progress Developer Studio for OpenEdge (PDS OE), which has support only for Windows. However, we can run ABLUnit test cases in any environment without PDS OE. Below are the steps to achieve this:

Main libraries:

OpenEdge.Core.pl - Contains Assertions required to work with ablunit.

ablunit.pl - Core framework along with a runner (ablunitcore.p), which is responsible for running unit tests cases.

Following are the steps to run

1. Copy procedure library ablunit.pl from <DLC>\tty and place it in linux environment. 2. Add the above copied libraries to PROPATH.

3. Run the test cases from PROENV.

Working with ABLUnit in pre-11.4 versions

Even though ABLUnit feature is supported from version 11.4, we can work with ABLUnit in pre-11.4 versions too from OpenEdge 11.0.

We can run ablunit test cases in any OpenEdge version from 11.0 by adding procedure libraries OpenEdge.Core.pl and ablunit.pl from 11.4 or later to pre-11.4 version’s PROPATH.

Conclusion

This article provides an introduction to writing and running tests with ABL Unit test. Initially, you may find that need to create lots of tests and you may feel it will slow you down. However, a small investment towards developing unit tests ultimately results in faster product releases and better quality products.

References

Related documents

buprenorphine/naloxone combination for induction, clinical experience in office-based trials conducted by the National Institute on Drug Abuse (NIDA) has demonstrated that

To determine which of the liver damage indices better ex- plains changes in methadone metabolism (Table 2), we compared them in terms of explanatory power (R 2 ) for

Thus, at every access made by the client to the site it will be classified according to some criteria in one of the pre-defined categories and the classification information will

Our Sundance label wines feature some of America's finest artisan winemakers, whose independent voices blend seamlessly with varietal subtleties.. found in

The elements of green infrastructure include green roofs, planters, rainwater harvesting, street trees, preserved open space on building sites, natural vegetated

We tested the hypothesis that long-term adaptation to the normal contingencies between walking and its multisensory consequences (including optic flow) leads to enhanced

Cardiff Council held a public consultation in spring 2013 on a proposal to establish a stand alone new 1.5 form entry (FE) English-medium primary school with a 32 full time