io.mats3.test.junit.Rule_MatsEndpoint Maven / Gradle / Ivy
Show all versions of mats-test-junit Show documentation
package io.mats3.test.junit;
import javax.inject.Inject;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import io.mats3.MatsEndpoint.ProcessSingleLambda;
import io.mats3.MatsFactory;
import io.mats3.test.abstractunit.AbstractMatsTestEndpoint;
/**
* Rule to create a single staged endpoint whose reply/processor can be changed throughout its life, i.e. per test (e.g.
* answer "Sorry, no can do." for the first test, and then "Yes, we can!" for the next test). Useful for mocking
* endpoints in tests where you need predictable replies, and may also be used to verify that an endpoint was
* not invoked.
*
* The endpoint processor can be changed on demand using {@link #setProcessLambda(ProcessSingleLambda)}
*
* Must be annotated with {@link Rule @Rule}. Do not use with {@link ClassRule @ClassRule}
*
* Retrieve the endpoint's received(incoming) message/messages by calling on of the following methods:
*
* - {@link Rule_MatsEndpoint#waitForRequest()} - Wait for a message(singular) using the default timeout
* - {@link Rule_MatsEndpoint#waitForRequest(long)} - Wait for a message(singular) with user specified timeout
* - {@link Rule_MatsEndpoint#waitForRequests(int)} - Wait for X messages using the default timeout
* - {@link Rule_MatsEndpoint#waitForRequests(int, long)} - Wait for X messages with user specified timeout
*
* Given a case where one does not expect the endpoint to be invoked (no messages received) one can utilize
* {@link Rule_MatsEndpoint#verifyNotInvoked()} to ensure that the endpoint was not in fact invoked during the test.
*
* If no process lambda is specified for the endpoint it will act as a terminator, thus it does not generate a reply.
*
*
*
* @Rule
* public Rule_MatsEndpoint<String, String> _world = Rule_MatsEndpoint.single(endpointFactory, "World",
* String.class, String.class, (context, in) -> in + "World");
*
*
* Should one want to utilize this test endpoint approach in a test which brings up a Spring context which contains a
* {@link MatsFactory} one can utilize the @SpringInjectRulesAndExtensions
(in 'mats-spring-test') which
* will inject/autowire this class automatically by providing the {@link MatsFactory} located in said Spring context.
*
* @param
* The reply class of the message generated by this endpoint. (Reply Class)
* @param
* The incoming message class for this endpoint. (Request Class)
* @author Kevin Mc Tiernan, 2020-10-22, [email protected]
*/
public class Rule_MatsEndpoint extends AbstractMatsTestEndpoint implements TestRule {
/**
* Private constructor, utilize {@link #create(String, Class, Class)} to create an instance of this object.
*/
private Rule_MatsEndpoint(String endpointId, Class replyMsgClass, Class incomingMsgClass) {
super(endpointId, replyMsgClass, incomingMsgClass);
}
/**
* Sets the internal {@link MatsFactory} to be utilized for the creation of this endpoint.
*
* If not utilized explicitly can also be injected/autowired through the use of the test execution listener
* @SpringInjectRulesAndExtensions
should this Rule be utilized in a test where a Spring context is in
* play.
*
* @param matsFactory
* to set.
* @return this instance of the object.
*/
@Inject
@Override
public Rule_MatsEndpoint setMatsFactory(MatsFactory matsFactory) {
_matsFactory = matsFactory;
return this;
}
@Override
public Rule_MatsEndpoint setProcessLambda(ProcessSingleLambda processLambda) {
_processLambda = processLambda;
return this;
}
/**
* Creates a JUnit Rule for a single-staged endpoint whose processor is not defined at start. Sets it up on
* JUnit lifecycle 'before' and tears it down on 'after'. Notice that a {@link MatsFactory} must be set before it
* is usable! In a Spring environment, you should probably employ the
* @SpringInjectRulesAndExtensions
to make this happen automagically. In a "pure Java" environment,
* consider the convenience overload {@link #create(Rule_Mats, String, Class, Class) create(Mats_Rule, endpointId,
* replyClass, incomingClass)} to easily supply the corresponding {@literal @ClassRule}
* {@link Rule_Mats} for fetching the MatsFactory
.
*
* Do notice that you need to invoke {@link #setProcessLambda(ProcessSingleLambda)} - typically inside the
* {@literal @Test}
method - before sending messages to it, as there is no default.
*
* @param endpointId
* of the endpoint.
* @param replyMsgClass
* the class of the reply message generated by this endpoint.
* @param incomingMsgClass
* the incoming message class for this endpoint.
* @return {@link Rule_MatsEndpoint}
*/
public static Rule_MatsEndpoint create(String endpointId, Class replyMsgClass,
Class incomingMsgClass) {
return new Rule_MatsEndpoint<>(endpointId, replyMsgClass, incomingMsgClass);
}
/**
* Convenience variant of {@link #create(String, Class, Class) create(endpointId, replyClass, incomingClass)} taking
* a {@link Rule_Mats} as first argument for fetching the {@link MatsFactory}, for use in "pure Java" environments
* (read as: non-Spring).
*/
public static Rule_MatsEndpoint create(Rule_Mats matsRule, String endpointId, Class replyMsgClass,
Class incomingMsgClass) {
Rule_MatsEndpoint rule_matsEndpoint = new Rule_MatsEndpoint<>(endpointId, replyMsgClass,
incomingMsgClass);
// Set MatsFactory from the supplied Rule_Mats
rule_matsEndpoint.setMatsFactory(matsRule.getMatsFactory());
return rule_matsEndpoint;
}
// ================== Junit LifeCycle =============================================================================
/**
* Note: Shamelessly inspired from: How to combine @Rule
* and @ClassRule in JUnit 4.12
*/
@Override
public Statement apply(Statement base, Description description) {
if (description.isSuite()) {
throw new IllegalStateException("The Rule_MatsEndpoint should be applied as a @Rule, NOT as a @ClassRule");
}
return new Statement() {
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
}
finally {
after();
}
}
};
}
}