5.3. Source Stubs

Source Stubs offer the most fine-grained control over what code gets executed in the testing context. Using them you can prevent any line or block of code from being executed whilst testing (i.e. whilst running Exemplars and JUnits controlled by Sureassert UC). You can also replace the stubbed code with alternate code that will only be executed in the test context, or insert new code for use only in the test context.

Source stubs are defined within comments so that they do not interfere with the normal running of the production code. This means that Eclipse will not highlight compiler errors about your code in the usual way, as you type. Sureassert will however report back any compilation errors received from the Java compiler as Sureassert UC errors whenever you save your code.

Importantly, source stubbing allows you to stub out calls to imported library classes as well as your own project classes: you can stub any code with source stubs.

5.3.1. Using In-Line @Stub

Stub out a line of code by inserting [@Stub] in a comment preceding the line to be stubbed:

@Exemplar(args={"Booking/1"}, expect="$arg1.isCancelled")
public void cancelBooking(Booking booking) throws SeatNotFoundException {

    ...
    try {
        List seatIDs = booking.getSeatIDs();
        for (int seatID : seatIDs) {
            // [@Stub]
            seatServiceDelegate.cancelSeat(seatID);
        }
        booking.setCancelled();
    } catch (NamingException e) {
        throw new TechnicalException(e);
    } catch (RemoteException e) {
        throw new TechnicalException(e);
    }
    ...
}

The seatServiceDelegate.cancelSeat(seatID); line would not get executed when the Exemplar runs in this example because the // [@Stub] has been introduced. Similarly, you can stub out blocks of code:

// [@Stub]
for (int seatID : seatIDs) {
    seatServiceDelegate.cancelSeat(seatID);
}

If necessary, you can introduce a block into your code purely for the purpose of making it possible to stub out that block:

// [@Stub]
{
    for (int seatID : seatIDs) {
        seatServiceDelegate.cancelSeat(seatID);
    }
    booking.setCancelled();
}

Sureassert UC will highlight the code that has been stubbed (removed from the test context) after you save the code.

5.3.1.1. Replacing Stubbed Code with Test Code

You can specify code to replace the stubbed code in the test context within the [@Stub]. For instance to call an overloaded method instead of the original one in the test context, you might specify:

// [@Stub seatServiceDelegate.cancelSeat(seatID, true); ]
seatServiceDelegate.cancelSeat(seatID);

You can also specify the replacement code for stubbed statements or blocks across multiple lines:

/* [@Stub
*     System.out.println("Altered seat " + seatID);
*	seatServiceDelegate.cancelSeat(seatID, true);
*  ]   */
seatServiceDelegate.cancelSeat(seatID);

You can stub and replace any code within the source file: within methods, fields, inner classes, etc.

5.3.2. Using In-Line @StubInsert

Code can be inserted into the test context without removing any production code if required. To do this use [@StubInsert …]

// [@StubInsert System.out.println("Altered seat " + seatID);]
seatServiceDelegate.cancelSeat(seatID);

5.3.3. Using Stub Variables

It is possible to define expressions within your Exemplar that you can refer to within source stubs. To define a source stub, use the notation:

stubVariableName  = resultExpression

from within a stubs definition in your Exemplar. The resultExpression can be a simple value or any SIN Expression. For example to set stub variable myStubVar to a list containing the integers 1,2,3:

stubs="myStubVar = l:1,2,3"

Or to set it based on a value returned by a test utility class method:

stubs="myStubVar = StubUtils.getMyStubVar()"

The result expression is evaluated/executed at the point the stub variable is used at runtime during Exemplar execution.

To reference the stub variable within stub code, use the notation [$stubVariableName] within the stub code defined in a @Stub or @StubInsert. For example, to set the seat IDs cancelled by the cancelBooking method to an empty list (for the purposes of illustration…) you could do the following:

@Exemplar(name="exemplar1", args={"Booking/1"}, stubs={"stubSeatIDs=l:"})
public void cancelBooking(Booking booking) throws SeatNotFoundException {

    ...
    try {
        // [@Stub seatIDs = List seatIDs = [$stubSeatIDs]; ]
        List seatIDs = booking.getSeatIDs();

        for (int seatID : seatIDs) {
            seatServiceDelegate.cancelSeat(seatID);
        }
        booking.setCancelled();
    } catch (NamingException e) {
        throw new TechnicalException(e);
    } catch (RemoteException e) {
        throw new TechnicalException(e);
    }
    ...
}

In this example, the source stub is defined on the same method the Exemplar is defined on. This need not be the case: the source stub can exist anywhere within your workspace and refer to a stub variable defined within potentially multiple Exemplars in multiple classes.

5.3.4. Making Source Stubs Conditional

You can insert conditional statements within stub code just like any other code. For example to stub out calls to the cancelSeat method only where the seatID is 1:

for (int seatID : seatIDs) {
    // [@StubInsert if (seatID != 1) { ]
    seatServiceDelegate.cancelSeat(seatID);
    // [@StubInsert } ]
}

Additionally, you can make stubs conditional on whether a stub variable has been specified by the Exemplar being executed. This is achieved by using [?variableName] within [@Stub] or [@StubInsert] code where variableName is the name of a stub variable. This resolves to boolean true if the stub variable is defined by the current Exemplar, or otherwise false.

In the following example exemplar1 defines stub variable “stubSeatID” and exemplar 2 doesn’t. When exemplar 1 executes the code snippet, it’ll skip cancelling seat 1 because it assigns the value 1 to stubSeatID. When exemplar 2 executes no code will be stubbed because stubSeatID isn’t defined.

@Exemplars(set={
@Exemplar(name="exemplar1", args={"Booking/1"}, stubs={"stubSeatID=1"}),
@Exemplar(name="exemplar2", args={"Booking/1"}) })
public void cancelBooking(Booking booking) throws SeatNotFoundException {

    ...
    try {
        List seatIDs = booking.getSeatIDs();
        for (int seatID : seatIDs) {
            // [@StubInsert if ([?stubSeatID] && seatID != [$stubSeatID]) { ]
            seatServiceDelegate.cancelSeat(seatID);
            // [@StubInsert } ]
        }
        booking.setCancelled();
    } catch (NamingException e) {
        throw new TechnicalException(e);
    } catch (RemoteException e) {
        throw new TechnicalException(e);
    }
    ...
}

5.3.5. Configuration

You can disable source stubs from the Sureassert UC preferences page (Window->Preferences->Sureassert UC).

Previous Page   |   Next Page   |   Table of Contents

Comments are closed.