Paul Hammant's Blog: Wire Mocking Without A Service Virtualization Framework
Starting with the project made for a blog entry from a week ago, I have added some wire mocking that I want to talk about. It was already a fairly lean solution in terms of dependencies, as the ‘storage’ for the Todos (it was a todo-list app) was anemic already. But I wanted to try out mocking the API calls from the browser to the server completely. In this case it isn’t going to be any faster, but in enterprise development teams it nearly always will be.
The previous experiment with Edgar Espina’s Jooby backend for Pete Hodgson’s “TodoMVC backend” thing was deliberate. What Jooby technology offers is an exceptionally elegant composition API and a simple instansiabiliy in a testing setting. Specifically - a Jooby server can be wholly instantiated between tests, with a different setup for each. And it is that setup fidelity that I have used for this test that should answer the “why not use real services” that people ask.
First the server component
(lines removed for brevity)
public class App extends Jooby {
{
/* Todo API: */
use("/todos")
/* List all todos. */
.get(this::getAllTodos);
assets("/**");
}
protected Object getAllTodos() {
TodoStore store = require(TodoStore.class);
return store.list();
}
public static void main(final String[] args) {
run(App::new, args);
}
}
Beautifully terse and expressive, huh? Why did we waste 20 years with J2EE technologies?
Then the test itself
(lines removed for brevity)
public class TodoWebDriverTest {
public static class TestApp extends App {
private boolean appStarted;
public TestApp() {
onStarted(() -> appStarted = true);
}
@Override
protected Object getAllTodos() {
throw new UnsupportedOperationException("not expected");
}
}
private static ChromeDriver DRIVER;
private static FluentWebDriver FWD;
private static int testNum;
private TestApp app;
@BeforeClass
public static void sharedForAllTests() {
// Keep the WebDriver browser window open between tests
DRIVER = new ChromeDriver();
FWD = new FluentWebDriver(DRIVER);
}
@AfterClass
public static void tearDown() {
DRIVER.close();
DRIVER.quit();
}
private String domain;
@Before
public void perTest() {
// anySubDomainOf.devd.io maps to 127.0.0.1
// I sure hope those people don't let the domain go, or remap it
// it is a decent way to ensure nothing is shared between tests (mostly)
domain = "http://t" + testNum++ + ".devd.io:8080";
todo = null;
id = -1;
}
@After
public void stopServer() {
app.stop();
}
@Test
public void initialListShouldBeASingleSetupEntry() {
app = new TestApp() {
/*
Mocking out a 'real' method.
*/
@Override
public Object getAllTodos() {
return new ArrayList<Todo>() {{
add(new Todo().setTitle("Win Lottery").setId(1));
add(new Todo().setTitle("Climb Everest").setId(2));
}};
}
};
startApp();
openTodoPage();
listInPageShouldBe("Win Lottery|Climb Everest");
}
private void openTodoPage() {
DRIVER.get(domain + "/index.html?" + domain + "/todos");
}
private void startApp() {
app.start("server.join=false");
while (!app.appStarted) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
}
}
}
private static void listInPageShouldBe(String shouldBe) {
FWD.ul(id("todo-list"))
.getText((TestableString.StringChanger) s -> s.replace("\n", "|"))
.within(secs(1)).shouldBe(shouldBe);
}
}
So the WebDriver window stays open the whole test. I only show one test method above but if there were more they would share the same browser window. Closing and reopening the browser window between tests is so slow.
The mocked out wire operation is still using Jooby for the RESTful interop, and is reusing the same plain Java classes for the payload. This is what allows the testing technique to be terse. I will claim it allows for elegance too.
Have a look at the whole diff that introduces the mock usage for tests.
Open source wire mocking frameworks
In a recent job I commissioned multiple new web apps and insisted that they were developed with Mountebank which is a swiss army knife of such things. It is relatively easily programmable via a RESTful call to an admin interface. There are technologies like Javabank that allow you to hide the complexities of dealing with REST from Java, and it all ends up being quite robust.
That is one more process to orchestrate, though, even if we do fit the shared nothing constraint for fast builds.
Other open source technologies include Hoverfly, WireMock, Bokor, Wilma, and Terminator.