Using this module, you can mock built-in macros. The reason to mock a built-in macro is mainly for testing purposes. When you develop a macro package collecting information from different sources
-
those may not be available at all in the test environment
-
may function in the test environment differently,
-
may be expensive to set up an environment where these built-in macros function as needed for the test.
The solution is to mock the built-in macros.
The primary goal developing this module was to provide mocking possibility for the Java::Geci macros. That way you can develop the code generator templates in IntelliJ using the Asciidoc plugin. When the built-in macros of Java::Geci are mocked you can see the generated code in the WYSIWYG editor.
To use this module, you have to add the dependency to your Maven project, as:
<dependency>
<groupId>com.javax0.jamal</groupId>
<artifactId>jamal-mock</artifactId>
<version>2.6.1-SNAPSHOT</version>
</dependency>
or use the maven:load
macro to load the dependency from Maven Central.
Mock can be used to create a mock for a built-in macro.
The actual macro may or may not exist prior.
It will be created by the mock
macro and defined in the current scope overriding the existing macro or shadowing any defined on higher scopes.
Any built-in macro can be mocked, even core macros like if
, import
, comment
.
The reason to mock a built-in macro is to test some macro code in a test environment where the original macro is not available or does not have its environment to function properly.
The use of the macro is simple. You have to define the name of the macro you want to mock and the output.
{@mock (macro=w)ajaja}{@w}
outputs .output
ajaja
You can override existing macros, even the core built-in ones. The mocks are only available on the current scope.
{#ident {@mock (macro=comment)comment is overridden}{@comment}}{@comment is not overridden here}
outputs .output
comment is overridden
You can also define multiple responses for a single mock. These responses will be used in the other they are defined.
{@mock (macro=ww)1}{@mock (macro=ww)2}{@ww}{@ww}
outputs .output
12
If you use a mock more times it has defined values it will result syntax error.
{@try! {@mock (macro=z)1}{@z}{@z}}
outputs .output
Mock z has exhausted after 1 uses.
Exhausted mock shadowing a defined built-in macro will not throw an exception though. It will simply use the original macro.
{@mock (macro=ident)exhausted}\
{@mock (macro=ident)macros}\
{@ident whatever is here, mocked} {@ident second time also mocked} {@ident are not mocked anymore}
outputs .output
exhausted macros are not mocked anymore
If you want to use a mock multiple times resulting the same value you can use the repeat
option.
{@mock (macro=w repeat=2)A}{@w}{@w}
outputs .output
AA
In addition to the repeat option it is also possible to use the infinite
option meaning: repeat as many times as needed.
{@mock (macro=w infinite)X}{@w}{@w}{@w}{@w}{@w}{@w}{@w}{@w}{@w}
outputs .output
XXXXXXXXX
You can also define multiple mocks for the same macro and give a regular expression to each. When the regular expression matches the input of the macro the one will be used. If there are more than one mock that would match the input then the one first defined and not exhausted yet are used.
{@mock (macro=q when=".*bee.*")bee}{@comment defined first, but used second}
{@mock (macro=q when=".*apple.*")apple}
{@q this is an apple and}
{@q there is a bee}
outputs .output
apple
bee
In the next section, we will describe all these options in details (reference) and also the formal argument how a mock response is selected.
-
macro
(aliasid
) the identifier of the macro. This an option is mandatory and has to define the identifier of the macro to be mocked. -
when
regular expression when to apply the mock. This option is not mandatory. In case it is specified, the mock response will only be used when the input of the macro matches the regular expression specified. If the option is missing, the mock response will always be matched and used when it gets activated regardless of the input of the macro. -
repeat
(aliastimes
) how many times the mock can be used. Can specify how many times the mock can be used. It is an error to use a negative number. You can use zero to switch off the mock response in your text temporarily without deleting it. -
inf
(aliasesinfinite
,forever
) if the mock be used infinite number of times. Can be used to specify that the mock response can be used unlimited number of times.
When the mock macro is used the input of the macro least the options between the (
and )
characters will be used as a response.
A new use of the mock
macro for the same macro
name will add a new response to the mock.
The responses are used in the order they are defined.
Every mock response has a counter and a regular expression selector.
-
The counter starts from one unless the option
repeat
specifies a different number or the optioninfinite
is used. In case the mock response is set toinfinite
the counter starts from "infinite". -
The regular expression selector can be defined using the option
when
. The response will only be used when the input of the macro mocked matches the regular expression. The regular expression should match the whole input and not only part of it. If you want to match only a part of it, you should use a regular expression that starts and ends with the.*
pattern fragment.The default pattern matches every use.
When a mock response is needed the selection algorithm starts from the first response defined and progresses towards the last. It will select the response whose regular expression selector matches the input of the macro and the counter has not reached zero. When a response is selected the counter is decreased.
It is an error specifying a new response for a macro following an infinitely repeatable response without selector.
As an extreme, though not practical use you can mock the macro mock
itself.
The example below mocks the macro mock
, then it is used, therefore the macro ident
is not mocked.
After that the macro comment
is mocked again, as the mocking of mock
is exhausted.
mock
{@mock (macro=mock)mock the mock once}{@mock (macro=ident)}{@mock (macro=comment)this is a comment}
{#ident not mocked}
{#comment mocked, does not matter what I write here}
outputs .output
mock the mock once
not mocked
this is a comment