2022 June Release

app.ducx Unit Test LanguagePermanent link for this heading

The purpose of the app.ducx unit test language is to simplify the definition of unit tests.

Using the app.ducx unit test language, you can define the build blocks for unit testing:

  • unit tests
  • unit test groups
  • scenarios

A unit test block consists of import declarations and unit test elements. The unittests keyword denotes a unit test block. It must be followed by the reference of your software component and curly braces.

Every file with a .ducx-ut extension can hold exactly one unit test block.

Syntax

unittests softwarecomponent
{
  // Import declarations
  import softwarecomponent;

  // Unit test elements (scenarios, unit tests, unit test groups)
  ...
}

Defining a Unit TestPermanent link for this heading

The basic element of the unit test language is the unit test itself. Every unit test can have global variables, an optional setup expression, a test expression and an optional cleanup expression. Additionally, some test contents can be assigned to the unit test.

Syntax

test reference {
  // global variables
  User theuser;

  init = expression {
    // optional init expression
    theuser = #User.ObjectCreate();
  }

  expression {
    // test expression
  }

  cleanup = expression {
    // optional cleanup expression
  }

  testdata<tdid, tdcontent> = {
    { "word", file("resources/testdata/word.docx") }
  }
}

Variables in Expressions

All expressions share the same local scope and can see the global variables of the scenario (see below) and the global variables of the unit test.

Evaluation

When evaluating a unit test, first the scenario is initialized if provided, then the setup expression, the test expression and the cleanup expression are evaluated. The cleanup expression is evaluated regardless of the result of the test expression.

Defining a Unit Test GroupPermanent link for this heading

Unit tests can be assigned to unit test groups to evaluate a group of unit tests together.

Syntax

testgroup UTGAllTests {
  UTFirstTest;
  UTSecondTest;
}

Defining a ScenarioPermanent link for this heading

Often different unit tests use the same basic setup. To ensure consistent tests, the init and cleanup code for these tests can be factored out into a so-called scenario.

A scenario consists of some global variables, an expression to initialize the scenario and a cleanup expression.

A unit test can use a scenario to define the setting.

Example

scenario SCOneUser {
  User theuser;

  expression {
    theuser = #User.ObjectCreate();
  }

  cleanup = expression {
     theuser.ObjectDelete();
  }
}

test UTWithUser using SCOneUser {
  expression {
    string subject = theuser.objsubject;
  }
}

Scenarios can be based on another scenario, thus building a hierarchy of scenarios.

Example

scenario SCBase {
  Folder basefolder;

  expression {
    basefolder = #Folder.ObjectCreate();
  }

  cleanup = expression {
     basefolder.ObjectDelete();
  }
}

scenario SCDetail: SCBase {
  Folder detailfolder;

  expression {
    detailfolder = Folder.ObjectCreate();
    basefolder.ObjectLock(true, true);
    basefolder.objchildren += detailfolder;
  }

  cleanup = expression {
     detailfolder.ObjectDelete();
  }
}

test UTWithBothUsers using SCDetail {
  expression {
    string combinednames = basefolder.objname + detailfolder.objname;
  }
}

Unittests in Production EnvironmentsPermanent link for this heading

In production environments the base components for unit testing are not installed. This is taken care of by generating the resulting software component in a way that unit test objects and scenario object are not loaded into such environments.

Note: This mechanism does not take care of component dependencies. All imported components are marked as prerequisites. If a component is used in unit tests and scenarios only, it is recommended to specify this component as optional. The result is a software component that can be loaded, even if some or all of the optional dependencies are not present.

ExamplePermanent link for this heading

This chapter describes a unit test scenario with all features available to the developer.

Scenario

The app needs two users and a group to test all the functionality, so it is a good idea to create a scenario which is used in all tests.

Example

scenario SCTwoUsers {
  User meyer;
  User mueller;

  init = expression {
    CreateUserData @userdata;
    @userdata.crfirstname = "Julia <~testscope~>";
    @userdata.crsurname = "Meyer";
    @userdata.crlogname = "julia.meyer.<~sessionid~><~testscope~>@pair.com";
    meyer = coouser.CreateTestUser(@userdata);
    mueller = coouser.CreateTestUser(null);
  }
}

Note: To create users there is a special usecase FSCDUCXUNIT@1.1001:CreateTestUser. This usecase takes some initialization data and creates a valid user in the current environment.

Scenario Hierarchy

To provide even more flexibility, a scenario can be based on another scenario, giving a hierarchy of scenarios.

Example

scenario SCOneGroupTwoUsers: SCTwoUsers {
  Group pair;

  init = expression {
    CreateGroupData @groupdata = {
      crmembers: [meyer, mueller],
      crshortname: "Pair <~testscope~>"
    };
    pair = coouser.CreateTestGroup(@groupdata);
  }
}

Note: To create groups there is a special usecase FSCDUCXUNIT@1.1001:CreateTestGroup. This usecase takes some initialization data and creates a valid group in the current environment.

Unit Tests

All unit tests can use this scenario to create their test environment.

Example

test UTFolder using SCOneGroupTwoUsers {
  string foldername;

  init = expression {
    foldername = "TheFolderName";
    cootx.Commit();
  }

  test = expression {
    %%ASSERT(meyer in pair.grmembers);
    %%ASSERT(mueller in pair.grmembers);
    coouser.SwitchToUser(meyer);
    %%ASSERT(coort.GetCurrentUser().GetAddress() == meyer.GetAddress());
    Folder fld = #Folder.ObjectCreate();
    fld.objname = foldername + ::sessionid;
    cooroot.ObjectLock(true, true);
    cooroot.objchildren += fld;
    cootx.Commit();
    fld.AddToTestSession();
    coouser.SwitchToUser(mueller);
    %%ASSERT(coort.GetCurrentUser().GetAddress() == mueller.GetAddress());
    cooroot.ObjectLock(true, true);
    cooroot.objchildren += fld;
    cootx.Commit();
    coouser.SwitchToUser();
  }
}

Note: After executing the test, the created objects need to be deleted, regardless of the success or failure of the test. This can be achieved by registering the new objects to the test session with the use case FSCDUCXUNIT@1.1001:AddToTestSession.

Session Start and Stop

Even in app.test these scenarios can be used to create the environment. All the required data for executing the tests are provided as parameters.

Example

<Sequence clienttype="External" jarfile="apptest-commander.jar" classname="Commander">
  <Execution action='Select' location='Commands.StartScenario'/>
  <Execution action='Set' location='Commands.StartScenario.Scenario' value='FSCDUCXUNIT@1.1001:SCOneGroupTwoUsers'/>
  <Execution action='Set' location='Commands.StartScenario.Address' value='{~Run.address~}'/>
  <Execution action='Set' location='Commands.StartScenario.Username' value='Administrator'/>
  <Execution action='Set' location='Commands.StartScenario.SessionId' value='abc'/>
  <Execution action='Set' location='Commands.StartScenario.Password' value='{~Login.password~}'/>
  <Execution action='Set' location='Commands.StartScenario.DefaultUserPassword' value='{~Login.password~}'/>
  <Execution action='Set' location='Commands.StartScenario.SSLVerify' value='false'/>
  <Execution action='Execute' location='Commands.StartScenario'/>
  <Set parameter='id' location='Commands.StartScenario.CreatedSessionId'/>
  <Set parameter='sessionerror' location='Commands.StartScenario.ErrorMessage'/>
  <Validation ok='"{~sessionerror~}"!=""' />
  <Execution action='Close' location='Commands.StartScenario'/>
</Sequence>

To remove the created objects the session can be closed, too.

Example

<Sequence clienttype="External" jarfile="apptest-commander.jar" classname="Commander">
  <Execution action='Select' location='Commands.StopScenario'/>
  <Execution action='Set' location='Commands.StopScenario.SessionId' value='{~id~}'/>
  <Execution action='Set' location='Commands.StopScenario.Address' value='{~Run.address~}'/>
  <Execution action='Set' location='Commands.StopScenario.Username' value='Administrator'/>
  <Execution action='Set' location='Commands.StopScenario.Password' value='{~Login.password~}'/>
  <Execution action='Set' location='Commands.StopScenario.SSLVerify' value='false'/>
  <Execution action='Execute' location='Commands.StopScenario'/>
  <Set parameter='sessionerror' location='Commands.StopScenario.ErrorMessage'/>
  <Validation ok='"{~sessionerror~}"==""' />
  <Execution action='Close' location='Commands.StopScenario'/>
</Sequence>