Converting a Future[T] to an always-successful Future[Option[T]] - web-services

The motivating use-case I have in mind is as follows:
In a Play Framework controller, I am calling Play's WS. This produces a potentially-failing Future
I want to handle the separate cases of the WS request succeeding vs failing. In both cases, I want to produce a Play Response eventually (likely a Future[Response])
What I think I am trying to do is:
Asynchronously, after the future completes, unconditionally handle the completion
Produce a Future[Response] corresponding to the desired response
I don't believe I can use Future.map because I need to customize the handling of the failure case and not just pass on the Future's failure.
If you have any alternative suggestions for how to solve this cleanly, please let me know.

You're looking for Future.recover.
val wsResponse: Future[WSResponse] = ???
wsResponse map { response =>
// Success case
Ok(response.json)
} recover {
// Failure case, turn your throwable into a response
case t: Throwable =>
InternalServerError(t.getMessage)
}
recover takes a partial function Throwable => T. In this case, T will probably be an Option[Response] so you can construct that according to whatever your business logic is in both the success and fail cases.
Do note that Play's WS library will return a successful future for a failed HTTP call, if (e.g.,) your call returns a 404 from the external server, so your map function will still need some error handling.

Related

Do we need assertions in addition to the provider interaction declaration with Pact?

I'm working on pact and I'm often see example that do assertions on request's response along the contract testing part like
test('Check endpoint for post questions on success', async () => {
await provider.addInteraction(…);
const response = await createQuestion(…);
expect(response.status).toBe(201); // <------------
});
Question
Is there added value, to contract testing, to add such assertions on the request's response? Or declaring the interaction embed the whole value of contract testing.
In short, no.
I wouldn’t do that assertion personally, as all that that is just checking is that Pact did what you asked it to do - i.e. testing the mock.
Think of a Pact test as a unit test of your API client, that just so happens to also produce a contract.
Therefore, it’s much more important to check the behaviour of your API client being tested, and Pact will actually check the real response of the provider later on.
In your case, I'd expect createQuestion to return some useful data (or potentially a Question domain model), so you should check that object is correct. e.g.
const question = await createQuestion(...);
expect(question.id).toBeDefined();

Accessing request container (event_dispatcher) within a test client

I created a simple test case in Symfony.
So one client which should listen for an event which will be dispatched during an request.
But nothing happen because the request have an own scope or I dont know why Im not able to access the dispatcher in it.
$this->client = static::createClient();
self::$container = $this->client->getContainer();
$dispatcher = self::$container->get('event_dispatcher');
$dispatcher->addListener('example', function ($event) {
// Never executed
});
$this->client->request('POST', $endpoint, $this->getNextRequestParameters($i), [$file], $this->requestHeaders);
$this->client->getResponse();
The listener is never called.
When I debug it a bit I find out that the object hash via spl_object_hash($dispatcher) is different on the highest level than on within the request level.
So it seems that the request has an own world and ignores everything outside.
But then is the question how I can put my listener to this "world"?
I think part of the problem is the mixing of testing styles. You have a WebTestCase which is intended for a very high level of testing (requests & responses). It should not really care about internals, i.e. which services or listeners are called. It only cares that given input x (your request) you will get output y (your response). This allows to ensure the basic functionality as perceived by your users is always met, without caring how it is done. Making these tests very flexible.
By looking into the container and the services you are going into a lower level of testing, which tests interconnected services. This is usually only done within the same process for the reasons you already found out. The higher level test has 2 separate lifecycles, one for the test itself and one for the simulated web request to your application, hence the different object ids.
The solution is either to emit something to the higher level, e.g. by setting headers or changing the output, so you can inspect the response body. You could also write into some log file and check the logs before/after the request for that message.
A different option would be to move the whole test into a lower level where you do not need the requests and instead only work with the services. For this you can use the KernelTestCase (instead of the WebTestCase) and instead of calling createClient() you call bootKernel. This will give you access to your container where you can modify the EventDispatcher. Rather than sending a request you can then either call the code directly, e.g. dispatch an event if you only want to test the listeners, or you can make your controller accessible as service and then manually create a request, call the action and then either check the response or whatever else you want to assert on. This could look roughly like this:
public function testActionFiresEvent()
{
$kernel = static::bootKernel();
$eventDispatcher = $kernel->getContainer()->get('event_dispatcher');
// ...
$request = Request::create();
// This might not work when the controller
// You can create a service configuration only used by tests,
// e.g. "config/services_test.yaml" and provide the controller service there
$controller = $kernel->getContainer()->get(MyController::class);
$response = $controller->endpointAction($request);
// ...Do assertions...
}

Writing unit test for make handler function in Go-kit

My problem is specific to Go-kit and how to organize code within.
I'm trying to write a unit test for the following function:
func MakeHandler(svc Service, logger kitlog.Logger) http.Handler {
orderHandler := kithttptransport.NewServer(
makeOrderEndpoint(svc),
decodeRequest,
encodeResponse,
)
r := mux.NewRouter()
r.Handle("/api/v1/order/", orderHandler).Methods("GET")
return r
}
What would be the correct way of writing a proper unit test? I have seen examples such as the following:
sMock := &ServiceMock{}
h := MakeHandler(sMock, log.NewNopLogger())
r := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/v1/order/", bytes.NewBuffer([]byte("{}")))
h.ServeHTTP(r, req)
And then testing the body and headers of the request. But this doesn't seem like a proper unit test, as calls other parts of the code (orderHandler). Is it possible to just validate what's returned from MakeHandler() instead of during a request?
TL;DR: Yes, that test is in the right direction. You shouldn't try to test the
internals of the returned handler since that third party package may change in ways you didn't expect in the future.
Is it possible to just validate what's returned from MakeHandler() instead of
during a request?
Not in a good way. MakeHandler() returns an interface and ideally you'd use
just the interface methods in your tests.
You could look at the docs of the type returned by mux.NewRouter() to see if
there are any fields or methods in the concrete type that can give you the
information, but that can turn out to be a pain - both for understanding the
tests (one more rarely used type to learn about) and due to how future
modifications to the mux package may affect your code without breaking the
tests.
What would be the correct way of writing a proper unit test?
Your example is actually in the right direction. When testing MakeHandler(),
you're testing that the handler returned by it is able to handle all the paths
and calls the correct handler for each path. So you need to call the
ServeHTTP() method, let it do its thing and then test to see it worked
correctly. Only introspecting the handler does not guarantee correctness during
actual usage.
You may need to make actually valid requests though so you're able to understand
which handler was called based on the response body or header. That should
bring the test to a quite reasonable state. (I think you already have that)
Similarly, I'd add a basic sub test for each route that's added in the future.
Detailed handler tests can be written in separate funcs.

How can you test code that relies on net.Conn without creating an actual network connection?

If I have code that works with a net.Conn, how can I write tests for it without actually creating a network connection to localhost?
I've seen no solutions to this online; people seem to either ignore it (no tests), write tests that cannot run in parallel (ie. use an actual network connection, which uses up ports), or use io.Pipe.
However, net.Conn defines SetReadDeadline, SetWriteDeadline; and io.Pipe doesn't. net.Pipe also doesnt, despite superficially claiming to implement the interface, it's simply implemented with:
func (p *pipe) SetDeadline(t time.Time) error {
return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (p *pipe) SetReadDeadline(t time.Time) error {
return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (p *pipe) SetWriteDeadline(t time.Time) error {
return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
(see: https://golang.org/src/net/pipe.go)
So... is there some other way of doing this?
I'll accept any answer that shows how to use a stream in a test with a working deadline that is not an actual network socket.
(Idly, this cloudflare blogpost covers the motivation for using deadlines, and why blocking forever in a goroutine per connection is not an acceptable solution; but regardless of that argument, particularly in this case I'm looking for a solution for tests where we deliberately want to handle edge cases where a bad connection hangs, etc.)
(NB. this may seem like a duplicate of Simulate a tcp connection in Go, but notice that all the solutions proposed in that question do not implement the Deadline functions, which is specifically what I'm asking about how to test here)
Your question is very open, so it is not possible to give you "the correct answer". But I think I understand the point where you stuck. This answer is also open, but it should bring you back on the right track.
A few days ago I wrote a short article, which shows the principle which you have to use.
Before I make some small examples how you such tests work, we need to fix one important point:
We do not test the net package. We asume, that the package has no bug and does, what the documentation says. That means we do not care how the Go team has implementet SetReadDeadline and SetWriteDeadline. We test only the usage in our programm.
Step 1: Refactoring your code
You did not post any code snippets, so I give you just a simple example. I guess you have a method or function, where you are using the net package.
func myConn(...) error {
// You code is here
c, err := net.Dial("tcp", "12.34.56.78:80")
c.setDeadline(t)
// More code here
}
That you are able to to test you need to refactor your function, so it is just using the net.Conn interface. To do this, the net.Dial() call has to be moved outside of the function. Remember that we don't want to test the net.Dial function.
The new function could look something like that:
func myConn(c, net.Conn, ...) error {
// You code is here
c.setDeadline(t)
// More code here
}
Step 2: Implement the net.Conn interface
For testing you need to implement the net.Conn interface:
type connTester struct {
deadline time.Time
}
func (c *connTester) Read(b []byte) (n int, err error) {
return 0, nil
}
...
func (c *connTester) SetDeadline(t time.Time) error {
c.deadline = t
return nil
}
...
Complete implementation including a small type check:
https://play.golang.org/p/taAmI61vVz
Step 3: Testing
When testing, we don't care about the Dial() Method, we just create a pointer to our testtype, which implements the net.Conn interface and put it into your function. Afterwards we look inside our test cases, if the deadline parameter is set correctly.
func TestMyConn(t *testing.T){
myconnTester = &connTester{}
err := myConn(myconnTester,...)
...
if myconntester.deadline != expectedDeadline{
//Test fails
}
}
So when testing, you should always think about, what feature you want to test. I think it is the really most difficult part to abstract the functionality you really want to write. Inside of simple unit tests you should never test functionalities of the standard library. Hope this examples can help you to bring you back on the right track.
Code that needs to be swapped out, for a controlled version in a unittest, should live behind an abstraction. In this case the abstraction would be the net.Conn interface. The production code would use go std lib net.Conn but the test code would use a test stub that is configured with the exact logic to exercise your function.
Introducing an abstraction is a powerful pattern that should allow swapping out all IO, or timing based code, to allow for controlled execution of code, during a unittest.
#apxp stated the same approach in a comment.
The same approach should work for the deadlines. It could get tricky simulating a deadline that is reached, because you may have to configure your stub with multiple responses. ie. The first response succeeds, but the second simulates a deadline that has been reached, and throws an error for the second request.

How to mock http.Head()

I'm studying the outyet example project from https://github.com/golang/example/tree/master/outyet. The test file does not cover the case where http.Head(url) returns an error. I would like to extend the unit tests to cover the if statement where the error is logged (https://github.com/golang/example/blob/master/outyet/main.go#L100). I would like to mock http.Head(), but I'm not sure how to do this. How can this be done?
The http.Head function simply calls the Head method on the default HTTP client (exposed as http.DefaultClient). By replacing the default client within your test, you can change the behaviour of these standard library functions.
In particular, you will want a client that sets a custom transport (any object implementing the http.RoundTripper interface). Something like the following:
type testTransport struct{}
func (t testTransport) RoundTrip(request *http.Request) (*http.Response, error) {
# Check expectations on request, and return an appropriate response
}
...
savedClient := http.DefaultClient
http.DefaultClient = &http.Client{
Transport: testTransport{},
}
# perform tests that call http.Head, http.Get, etc
http.DefaultClient = savedClient
You could also use this technique to mock network errors by returning an error from your transport rather than an HTTP response.