ProUnit
 

Developing your Tests

What's a Test Case?

A Test Case is just a Progress ABL (4GL) file that follows some simple rules about how to name the internal procedures. The code below shows a simple example of a Test Case file.


/*------------------------------------------------------------------------------
Some code to initialize the environment or database before running the test.
------------------------------------------------------------------------------*/
PROCEDURE initialize:
END.


/*------------------------------------------------------------------------------
Some code run before every test to reset internal states, if needed.
------------------------------------------------------------------------------*/
PROCEDURE setUp:
END.


/*------------------------------------------------------------------------------
Some code run after a test to restore, log or something else.
------------------------------------------------------------------------------*/
PROCEDURE tearDown:
END.


/*------------------------------------------------------------------------------
Dispose everything, free resource, close files, disconnect databases, etc.
------------------------------------------------------------------------------*/
PROCEDURE dispose:
END.


/*------------------------------------------------------------------------------
My First Test. Not that complex but should work fine!
------------------------------------------------------------------------------*/
PROCEDURE testMyFirstTest:
    RUN assertTrue(TRUE).
END.


/*------------------------------------------------------------------------------
Another test, more complex
------------------------------------------------------------------------------*/
PROCEDURE testMyAnotherTest:
    RUN recordEvent("Test #1 should be OK.").
    RUN assertTrue(TRUE).

    RUN recordEvent("Test #2 should NOT be OK.").
    RUN assertEqualsInt(10, 20).

    RUN recordEvent("Test #3 should be OK.").
    RUN assertEqualsChar("OK", "OK").
END.


/*------------------------------------------------------------------------------
My Third Test. Again, the test fails when X equals 7.
------------------------------------------------------------------------------*/
PROCEDURE testMyThirdTest:
    DEFINE VARIABLE X AS INTEGER     NO-UNDO.
    DO X = 1 TO 10000:
        RUN assertFalse(X MOD 7 = 0).
    END.
    RUN fail("This test has failed !").
END.


/*------------------------------------------------------------------------------
Another testing procedure.
------------------------------------------------------------------------------*/
PROCEDURE testScalar:
    RUN recordEvent("Tesing all scalar types.").
    RUN assertTrue(TRUE).
    RUN assertFalse(FALSE).
    RUN assertNull(?).
    RUN assertNotNull(SESSION).
    RUN assertEqualsChar("hello", "HELLO").
    RUN assertEqualsLongchar("hello", "HELLO").
    RUN assertEqualsInt(17, 17).
    RUN assertEqualsInt64(17, 17).
    RUN assertEqualsHandle(SESSION, SESSION).
    RUN assertEqualsDate(TODAY, TODAY).
    RUN assertEqualsDateTime(NOW, NOW).
    RUN assertEqualsDateTimeTZ(NOW, NOW).
    RUN assertEqualsDecimal(3.141592653589793236, 3.141592653589793236).
    RUN assertEqualsLogical(NO, NOT YES).
    RUN assertEqualsFile("c:\BOOTFONT.BIN", "c:\BOOTFONT.BIN").
    MESSAGE "This blocking message to sped some time." VIEW-AS ALERT-BOX INFORMATION.
    RUN assertQuicker(15).
END.

        


The table below explains all the procedures.

Test Case Procedures
Procedure Description
initialize Procedure called by ProUnit once before running any test in the program. Use it to allocate/locking resources, connect databases, etc.
setUp Procedure called by ProUnit once before running every test in the program. Use it to reset internal states, etc.
test<name> This is where you'll code the logic of your test. You may have many different tests in the same program but all of them must have their name starting by "test" (like testWithLoop and testSomething). You may have other internal procedures for logic reuse or to make the test more readable but just the test units must start with "test".
tearDown Procedure called by ProUnit after every test. Use it to clean up the environment.
dispose Procedure called by ProUnit to free all resources used by this Test Case (normally allocated by the initialized method).

Assertions and Failing

During your test you evaluate some conditions to be sure the code you're testing is running fine. For example, suppose you call the Sum method of a persistent object and then check if the returned value matches the value you expected. This is an Assertion. Basically it means: "Here I assume that something is true, or false, or that X equals Y", and if it's not true there is a bug.

ProUnit provides some different assert methods to ease your job:

ProUnit Assertion Methods
Assert Method Description Example
assertTrue Check if the parameter received is true. If not the test fails.
RUN AssertTrue(isConnected).
assertFalse Check if the parameter received is FALSE. If not the test fails.
RUN AssertTrue(AVAILABLE Order).
assertNull Used to assert that a handle is null.
RUN AssertNull(hTarget).
assertNotNull Used to assert that a handle points to a valid handle.
RUN AssertNotNull(hTarget).
assertEqualsChar Used to assert that two strings (CHARACTER) have the same value.
RUN AssertEqualsChar(cState, "SC").
assertEqualsInt Used to assert that two integer values have the same value.
RUN AssertEqualsInt(iCounter, 100).
assertEqualsHandle Used to assert that two handles point to the same program.
RUN AssertEqualsHandle(hTarget, hW).
assertEqualsDate Used to assert that two dates have the same value.
RUN AssertEqualsDate(dtTarget, TODAY).
assertEqualsDecimal Used to assert that two decimals values are the same.
RUN AssertEqualsDecimal(deTarget 10.0).
assertEqualsLogical Used to assert that two logical values are equals.
RUN AssertEqualsHandle(lValue, true).
assertEqualsTT Used to assert that 2 temp-tables contain the same data (not much, not less) except for some fields (optionnal).
RUN assertEqualsTT(hTT, hTTReference, "exception_field1,exception_field2").
assertEqualsStrictTT Used to assert that 2 temp-tables contain the same data and the same structure except for some fields (optionnal).
RUN assertEqualsStrictTT(hTT, hTTReference, "exception_field1,exception_field2").
assertEqualsLongchar Used to assert that 2 large strings (LONGCHAR) are the same.
RUN assertEqualsLongchar(lgchar, lgcharReference).
assertEqualsInt64 Used to assert that 2 large integers (INT64) are the same.
RUN assertEqualsInt64(i64, i64Reference).
assertEqualsDateTime Used to assert that 2 datetimes are the same.
RUN assertEqualsDateTime(dt, dtReference).
assertEqualsDateTimeTZ Used to assert that 2 datetimes with time-zone information are the same.
RUN assertEqualsDateTimeTZ(dtz, dtzReference).
assertEqualsMemptrContent Used to assert that 2 memptr contain the same data.
RUN assertEqualsMemptrContent(mem, memReference).
assertEqualsFile Used to assert that 2 files contain the same data.
RUN assertEqualsFile("d:\file1.dat", "d:\file_reference.dat").
assertQuicker Used to assert that current test took less than a maximum time in milli-seconds from its begin to now.
RUN assertQuicker(150).

Describing an expected behavior

You can specify the expected behavior for a test, like an ERROR, STOP or QUIT condition.

ProUnit Behavior Description Methods
expectError Signalizes that the test expects an error to be raised.
RUN expectError(YES).
expectStop Signalizes that a STOP condition is expected on the test.
RUN expectStop(YES).
expectQuit Signalizes that a QUIT is expected on the test.
RUN expectQuit(YES).

Failing a Test

Besides using assertions, you can also fail a test using the method fail(String msg). In this case you can pass a string message to help you debugging your code.

RUN fail("Houston, we have a problem.").

Recording an event

Sometimes, you just need to add a text in the additional message column. In this case you can pass a string message to inform you about something.

RUN recordEvent("Sonic the hedgehog really rocks!").

Architecture and integration to standalone Progress Session

In this part, you can see a diagram detailing ProUnit architecture and integration to your own framework (if any).

In case of N-Tiers architecture for your applications, please have a look to this page.