Skip to content

Understanding how the mocking works

Richard Tomlinson edited this page Sep 23, 2016 · 14 revisions

The AOP Framework uses principles from Aspect Oriented Programming (https://en.wikipedia.org/wiki/Aspect-oriented_programming) to intercept and alter service behaviour on a running platform without change to any deployed code.

The AOP framework has a number of key terms which are used to describe behaviour:

  • Point Cut - The point at which the supplied code cuts into the existing flow and inserts its behaviour. Within a point cut is an Intercept Point and Flow Position
  • Flow Position - The point in the flow based on the service name, its package and the value of the pipeline
  • Intercept Point - The point to invoke the Interceptor, whether its before or after a service or to invoke the Interceptor in place of the service.
  • Interceptor - The actual code to execute. This can be a mock service, logger, pipeline capture, timing tool, etc.
  • Advice - The combination of a Point Cut and an Interceptor

Intercept Points within a service flow

Here we have a sample three flow service steps. Imagine they are three flow steps within a service, each called one after the other:

Service Call Sequence

The execution of Service A flow step is followed by flow step calling service B then the last step calling service C.

Intercept Point - Before

As a first example, we want to alter the way that Service B operates and perform some action or call to a service before service B is called:

Service with an intercept before

By using the AOP framework before Intercept Point a service or call to some other functionality can happen before a service is invoked so thereby interacting with the pipeline to change the behaviour of Service B. Examples of such behaviour might be to set or change a particular pipeline variable that change the way in which Service B operates or to capture the pipeline before the service executes and changes it.

Intercept Point - After

Another alternative is to call a service after a service has been called:

Service with an intercept after

In this case, like before, the flow of the service calls is interrupted by a call to Service D, this time after service B has been called. Once complete it continues on to Service C maintaining the original flow. An example for doing this might be pipeline capture or to overwrite or alter the values returned by Service B. This is achieved using the after Intercept Point.

Intercept Point - Invoke

The third way of interacting is to simply replace Service B with Service D so that the invoke of the service is replaced with something else:

Service with an invoke intercept to replace the service

This is more usual to replace the behaviour of Service B by an alternative service, in this case service D might be a mock which returns some fixed value. This is achieved using the invoke Intercept Point.

An important note about intercepting is what happens when multiple interceptors are registered for the same Intercept Point and Flow Position:

  • For before and after Intercept Points, all interceptors at that Flow Position will execute. This allows multiple behaviour such as changing the pipeline and capturing at the same point.
  • For invoke only the first Interceptor to match will execute. This ensures single behaviour to replicate the service that's being replaced.

Interceptors to supply behaviour

Interceptors are responsible for actually doing something when the Point Cut has been applied and its applicable. Interceptors in the framework allow you to perform many different behaviours:

  • Mock a service to consistently return a value into the pipeline. This 'canned response' gives the easiest way to return data
  • Raise an exception instead of a service to interrupt flow and simulate an error
  • Capture the pipeline to a file at any point making it possible to save data and use it as the basis of tests
  • Perform checks within the flow by adding Assertions that can prove a service has been called
  • Use all of the above within an XML structure to define Behavioural Driven style of test that provides a way to describe multiple conditions and outcomes.

General principles

Follow these simple rules to ensure you apply the interceptors at the correct location:

  • If you want to use an assertion to verify behaviour of a service, use an Intercept Point of before or after and do not use invoke.
  • To mock a service and replace its functionality with something else, use an Intercept Point of invoke.
  • To insert into the pipeline or modify the pipeline contents without affecting any services, use a mock with an Intercept Point of before or after.

Anatomy of points around a service

Adding ints service

The service show, pub.math:addInts, gives an example of thinking about how to mock a service, thinking about the before, invoke and after

before and invoke

If we wanted to add something before this service, say as an assertion to check a value, we could use the following variables in a condition or function:

  • total
  • aValue
  • num1
  • num2

The same set of pipeline values applies when using invoke to replace the service. At both the before and invoke intercept points, the pipeline has been set up so that its ready for the service to be invoked and any association between pipeline and input has been performed.

after

The after intercept point has all of the pipeline values as the before and invoke and includes the output from the service (Either that explicitly stated in the service's outputs or inserted into the pipeline by the service). What it does not include are the output assignments for the service. So, in the example above, if we take total of 5 and an aValue of 4 at the after point the output value is now 9 (The output from the service) however total is still 5.

This is important to note and its misunderstanding could lead to failed assertions, etc if the condition is matching whats expected to be in the pipeline and not what the service is returning.

Anatomy of points around a Transformer

Adding ints transformer

This example is showing a service used as a transformer and the affect of intercepting around or instead of it. As you know, transformers are lightweight in terms of the pipeline content that they have access to. In this case, only the inputs and outputs are available such:

  • before and invoke would only see the num1 and num2 values that had been associated. The total and aValue would be null
  • after has access to the inputs (num1 and num2 in this case) and the output value. The assignment to total would not have occurred at that point so would still be null

In effect intercepting around a transformer has access only to what that transformer exposes as inputs and outputs.

Scope

Setting up a mock on an IS server with just one user works fine, but what happens when there are multiple users or multiple processes that may be trying to interact with the service that's being mocked? Scoping the applicability of the mock can prevent unexpected or unwanted behaviour occurring while potentially simplifying or eliminating the need for a pipeline expression. The framework offers the choice between scopes of Global, User and Session with defaults in most situations.

Global Scope

Global is where the mock would apply irrespective of who or what is interacting with it. Limiting interaction with the mock comes from using pipeline expressions to test the Flow Position for applicability (Ie, does the pipeline have the right values for this mock to apply or should the real service be used).

Session Scope

Just like regular browsing, when interacting with IS its possible to create and maintain a session such that only interactions only apply to that session. An example would be a test tool that creates a mock and receives a session cookie that it presents when setting up other mocks, executing the service under test and verifying the results. Only the user of that session cookie would see and interact with the mocks, all other users would interact with the real services.

User Scope

When the use of sessions is not applicable, such as long running interactions, multiple callers to the mocks or the tooling not supporting sessions, then its possible to define the interaction based on the authenticated user that defines the mock. For example, if a mock was created using Developer as the authenticated user then anyone else authenticating with Developer would use that mock. A user authenticated with Administrator or another account would not see the mock.

Flow Position Matching

The framework makes a decision on whether to use a mock by matching the Flow Position with that defined for the Interceptor. The default flow position matching uses the fully qualified service name as one of the tests within the PointCut (The other being a match for the pipeline content). Matching on the service name works in a lot of cases but what happens with the condition when mocking serviceGamma you want interactions from serviceAlpha to use the mock but calls from serviceBeta to use the real service.

The Calling Service Position matching allows the name of the calling package, namespace or fully qualified service name to act as the condition by which the mock is matched for use.