Skip to content

Instantly share code, notes, and snippets.

@mikeycmccarthy
Created May 20, 2012 10:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikeycmccarthy/56e2938c5d59ad2951c8 to your computer and use it in GitHub Desktop.
Save mikeycmccarthy/56e2938c5d59ad2951c8 to your computer and use it in GitHub Desktop.
Component Test Harness
package gamesys.xmpp.test.util;
import gamesys.xmpp.component.AbstractComponent;
import gamesys.xmpp.component.Component;
import gamesys.xmpp.component.ComponentLifecycleException;
import gamesys.xmpp.component.ComponentRegistrar;
import org.junit.After;
import org.junit.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import static org.junit.Assert.assertThat;
import static org.xmlmatchers.XmlMatchers.isEquivalentTo;
import static org.xmlmatchers.transform.XmlConverters.the;
/**
* Base class for testing components.
* <p>
* Testing components can be tricky and requires a certain amount of setup (e.g. in the case where mocking is employed,
* setting up a mock component factory and using that to initialize the component). It can be difficult to use a {@link
* org.hamcrest.Matcher} out of the box to verify packets as {@link org.xmpp.packet.IQ} for example does not override
* equals and often you won't care to match certain details in the packet, for example the id attribute.
* <p>
* Testing without using mock frameworks also brings its own problems, namely that {@link org.xmpp.component.AbstractComponent}
* is deliberately multi-threaded, and as such, unit test assertions will often be called before the component has done the work
* under test.
* <p>
* This class aims to tackle the problems above, and make component unit tests free of the additional noise needed around
* setup.
*/
public abstract class AbstractComponentTest {
private AbstractComponent componentUnderTest;
private final Logger log = LoggerFactory.getLogger(getClass());
private final StubComponentManager componentManager;
private final AbstractComponentTest.TestComponentRegistrar testComponentRegistrar;
protected AbstractComponentTest() {
componentManager = new StubComponentManager();
testComponentRegistrar = new TestComponentRegistrar(componentManager);
}
/**
* Registers the component under test with a component manager.
* <p>
* If any component setup is needed, for example setting mocks on the component, use {@link #setUpComponent()}
* which is guaranteed to be called before component registration with the component manager
*/
@Before
public final void registerComponent() {
setUpComponent();
this.componentUnderTest = getComponentUnderTest();
testComponentRegistrar.registerComponent(componentUnderTest);
}
@After
public final void deRegisterComponent() {
testComponentRegistrar.deRegisterComponent(componentUnderTest);
}
/**
* Provides a hook to perform any component setup needed before {@link #registerComponent()}
* is performed.
*/
protected void setUpComponent(){}
/**
* Overriden by subclasses to present the component that is under test.
* This harness guarantees to use the same instance of the component over the course of an individual test, and
* therefore subclasses do not need to worry about ensuring about managing component instances. It would be
* perfectly reasonably for a subclass to return a new instance of the component under test in their implementation
* of this method.
* @return The component under test
*/
protected abstract AbstractComponent getComponentUnderTest();
/**
* Asserts that the last packet sent by the component matches what is passed.
* @param expectedPacket The expected packet.
* @throws Exception
*/
protected final void assertSent(Packet expectedPacket) throws Exception {
assertThat(the(lastSentPacketXML()), isEquivalentTo(the(expectedPacket.toXML())));
}
/**
* Convenience method to process a packet that has come someone with a member ID, rather than a host.
* @param packet
*/
protected final void processPlayerPacket(Packet packet){
packet.setFrom(new JID("12345", "chat", null));
componentUnderTest.processPacket(packet);
}
private String lastSentPacketXML() throws Exception {
return componentManager.getLastSentPacketXML();
}
private class TestComponentRegistrar implements ComponentRegistrar {
public TestComponentRegistrar(StubComponentManager componentManager) {
ComponentManagerFactory.setComponentManager(componentManager);
}
@Override
public void registerComponent(Component component) {
try {
log.debug("Registering {}", component);
getComponentManager().addComponent(component.getSubDomain(), component);
} catch (ComponentException e) {
log.error("Exception while registering {} component", component.getSubDomain());
throw new ComponentLifecycleException(e);
}
}
@Override
public void deRegisterComponent(Component component) {
try {
getComponentManager().removeComponent(component.getSubDomain());
} catch (ComponentException e) {
log.error("Exception while deregistering {} component", component.getSubDomain());
throw new ComponentLifecycleException(e);
}
}
private ComponentManager getComponentManager() {
ComponentManager componentManager = ComponentManagerFactory.getComponentManager();
if (!(componentManager instanceof StubComponentManager)) {
throw new IllegalStateException("You are attempting to use a real component manager in a stub test. Ensure no-one has set the ComponentManagerFactory component manager");
}
return componentManager;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment