New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support method / object callbacks instead of class name #120
Comments
The problem with this is that the expectation is serialised to the server using JSON. Typically the server runs as a separate process and so has no access to local objects. So currently there is no way to support executing an instance object. What you can do as a work around is make sure the MockServer is running in the same JVM by using the API such as ServerAndClient then use static methods. However, this is pretty much a hack that I don't recommend as you would have no test separation. I'm going to close this ticket as it is not possible due to limitation in Java. |
However I could implement this using web sockets, so I'll re-open this ticket and implement it when I get a chance. |
You are right. I missed the fact that everything goes through the wire even when we use junit Rule and server runs inside the same JVM process. Websockets is nice solution for that. We simply need a way to invoke code on MockServerClient's side and pass created response back to server. I see also other option here (very similar to websockets) - expose some kind of server endpoint on client side (could be HTTP server). When API user records behavior (creates expectations) with instance of given interface (similar to ExpectationCallback) client could assign unique identifier and map this unique identifier to that expectation instance. That unique id could be later passed to server and then server finds that should invoke expectation it calls client giving id and gets HttpResponse back. This is how I see that now. Thank you for quick response. |
For now I think web sockets is the best approach as it works really nicely in Netty which is used for both the client and the server. I've previously implemented web sockets with Netty before and they provide a really solid and simple implementation. |
When using MockServer via the JUnit MockServerRule, is everything running in the same JVM such that passing a ExpectationCallback instance would be possible? In Spock tests, this would be quite powerful, as a Closure could be used to implement the callback... |
Although when using the MockServerRule everything is running in the same JVM. The MockServer is running in a separate set of threads managed by Netty and so it wouldn't be immediately straight forward to pass a closure in. In the next couple of weeks I'll start implementing a WebSocket approach as this would likely be the simplest solution even in the same JVM and would additionally work when the MockServer was in another JVM. I just need to improve the Ruby client and finish improving the web site / documentation then this issue is next on the list. |
May be we can also extend HttpCallback to include some kind of tag-id (or callback-id) - it will help to distinguish requests on server side with same callback (and callback will be able to use static map to get proper responce). |
Yes exactly the server may need to maintain a list (probably UUIDs) against WebSocket to know which socket to call back on. Alternatively it may also be possible avoid any hash map / lookup table and if the objects can be structured correctly. I'm just working on updating the documentation, once this is completed and the ruby client is fixed this is the next item on the list, and is likely to be completed in the next month or so. |
Some additional information about my use-cases. Maybe it will be helpful (or just interesting :) )
|
thanks code examples are always used to fully understand how people use things. The original use case for the callback class was completely different and it only required a single static class, but it should be possible to extend it to cover your requirements. I'll implement it so the object instance you pass in will have to implement a Single Abstract Methods (SAM) interface (i.e. interface with a single method). This will mean those using Java 6+ can just implement the interface and those using Java 8+ can pass in a closure if they like. Even though MockServer is compiled with Java 6 for maximum support this approach should make the API nice for those using Java 8. |
Note: see #160 and make sure the solution uses WebSockets so that function callbacks can be used from JavaScript in browser or node.js |
…for method callbacks - only covers the web socket part so far but not yet integrated into Java or JavaScript clients
@jamesdbloom any update on this ? are you planning to complete this anytime soon ? |
The dynamic callback have been completed, except the following items:
Neither of these two remaining items will change the API and so it should be safe to use this functionality. The Java, browser javascript and node javascript clients all support it now, as follows: Javaimport org.junit.Rule;
import org.junit.Test;
import org.mockserver.client.server.MockServerClient;
import org.mockserver.junit.MockServerRule;
import org.mockserver.mock.action.ExpectationCallback;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.mockserver.matchers.Times.exactly;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import static org.mockserver.model.StringBody.exact;
/**
* @author jamesdbloom
*/
public class DynamicMethodCallback {
@Rule
public MockServerRule mockServerRule = new MockServerRule(this);
private MockServerClient mockServerClient;
@Test
public void doSomethingJava7() {
mockServerClient
.when(
request()
.withMethod("POST")
.withPath("/login")
.withQueryStringParameter(
"returnUrl", "/account"
)
.withCookie(
"sessionId", "2By8LOhBmaW5nZXJwcmludCIlMDAzMW"
)
.withBody(exact("{username: 'foo', password: 'bar'}")),
exactly(1)
)
.callback(
new ExpectationCallback() {
public HttpResponse handle(HttpRequest httpRequest) {
return response()
.withStatusCode(401)
.withHeader(
"Content-Type", "application/json; charset=utf-8"
)
.withHeader(
"Cache-Control", "public, max-age=86400"
)
.withBody("{ message: 'incorrect username and password combination' }")
.withDelay(SECONDS, 1);
}
}
);
}
@Test
public void doSomethingJava8() {
mockServerClient
.when(
request()
.withMethod("POST")
.withPath("/login")
.withQueryStringParameter(
"returnUrl", "/account"
)
.withCookie(
"sessionId", "2By8LOhBmaW5nZXJwcmludCIlMDAzMW"
)
.withBody(exact("{username: 'foo', password: 'bar'}")),
exactly(1)
)
.callback(
(HttpRequest httpRequest) -> response()
.withStatusCode(401)
.withHeader(
"Content-Type", "application/json; charset=utf-8"
)
.withHeader(
"Cache-Control", "public, max-age=86400"
)
.withBody("{ message: 'incorrect username and password combination' }")
.withDelay(SECONDS, 1)
);
}
} JavascriptmockServerClient("localhost", 1080).mockWithCallback(
{
'method': 'GET',
'path': '/two'
},
function (request) {
// some callback logic
if (request.method === 'GET' && request.path === '/two') {
return {
'statusCode': 202,
'body': 'two'
};
} else {
return {
'statusCode': 406
};
}
}
)
.then(
function () {
// expectation setup now test something
},
function (error) {
// failed to setup expectation
}
); |
FYI your'll need to use mockserver-netty version 3.10.7, mockserver-grunt@1.0.41 or the latest docker container |
Hello @jamesdbloom, Your previous mockServerClient
.verify(
request()
.withMethod("POST")
.withPath("/login"),
VerificationTimes.once()
); More over, Is it the expected behavior? |
That is not expected behaviour and I will submit a fix very soon. |
…not record to the log and so broken the verify functionality
The bug you raised is now fixed, closing this ticket and migrating the remaining documentation part to #115 which is the next highest priority issue. |
Thanks for nice piece of software.
I have just started using MockServer I love it however I feel I miss one feature. While mocking I'm able to specify HttpCallback which will be executed on server invocation. However HttpCallback may contain only class name. For some use cases I would like something more dynamic. I wonder if you considered allowing to put instance rather than class into HttpCallback so user could define behavior more dynamically.
That would be more similar to Mockito's when(something).thenAnswer(code_to_execute_here)
What do you think?
The text was updated successfully, but these errors were encountered: