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.
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:
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.
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.