-
Notifications
You must be signed in to change notification settings - Fork 70
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
(How/should) we we use the mock server as a stub? #13
Comments
Goals:
Before we talk implementation, @BenSayers, @mboudreau - do these requirements sound right? |
@bethesque I'm not certain that we need to specify the difference between a mock and a stub server. In my approach, they are exactly the same, the only difference is if I'm developing with it or testing. As for the request/respond description, all of my mocks will have path and data regex since I don't care about exactness of variables as long as it's in the same format. |
@bethesque Replying to your thoughts on the other thread:
I've given this some thought on how this would work for our current use case. Ideally, the mock server should only have one response per request, but I can see that it would be easily possible to have more than one by messing up the matching. I believe that the mock service should give back a warning when this happens and that it should only return the 'newest' matching response so that test 'children' can highjack a request from a potential parent suite test. As for the matching, in our application, we have a restful api, which is where a lot of matching will happen based on the url, then we expect a json back of a specific format. For the URL matching, I quite like how angular ui-router does it mostly because it gives me the flexibility to simply add a variable (up to the next '/') or create a more complex regex for specificity. For every 'matcher', we need to also specify a fake object that gets returned/used when in need (ie. fake url for provider testing, a fake json object for consumer) that matches the requirement. If we can get this going, our pact tests would be a lot simpler and development much stronger. |
There is an important difference between a mock and a stub - you don't verify a stub. I believe a stub is what you are asking for - the mock service without verification. Correct me if this is not what you're asking for. I think @BenSayers said it well:
In fact, ideally, there should be at least two responses per request - one success, and one failure. If you're not testing the failure scenario, then you have untested paths inside your code. I would typically test the failure scenario with a unit test though, not an integrated test. Using the common route declaration syntax |
@BenSayers, I've been giving the idea of creating a stub server from the pact file some thought and while I really like the idea (it would solve a lot of problems), I'm not sure if I see a good way to share the interactions between unit tests and integration tests as their concerns are different. In a unit test, I want to assert that when I save model with ID 100, that the path it goes to is exactly "/models/100". However, in an integration test, I just care that The difference is in the setup. Unit test: helloProvider
.given("a thing with ID 1234 exists")
.uponReceiving("a request to update a thing")
.withRequest("put", "/things/1234", {name: 'Updated thing'})
.willRespondWith(200, {
"Content-Type": "application/json"
}, {
name: 'Updated thing'
}); Integration tests: helloProvider
.given("a thing with ID 1234 exists")
.uponReceiving("a request to update a thing")
.withRequest(
"put",
term("/\/things\/\d+/", "/things/1234"), # This should be a term
somethingLike({name: 'Updated thing'})) # This should be a somethingLike (type based matching)
.willRespondWith(200, {
"Content-Type": "application/json"
}, {
name: 'Updated thing'
}); Do you see the issue? Any ideas? |
I see the issue you are raising and agree its a concern. Let me ponder on it. Another question that was raised is how do you tell the server which provider state it should be serving for a particular endpoint. My first thought to solve this is to expose a url to allow you to change provider states from within the integration tests. But that may quickly become cumbersome if you have many providers with many endpoints. There may be a need for the ability to tag states to allow the integration test to change all endpoints from all providers to the states tagged with a particular tag. @bethesque I assume you have projects that use Pact that have the need for a stub server of this kind for integration testing. I'd like to understand how you solve this problem today, let me bombard you with questions:
|
Hi Ben,
Most of the projects I have worked with Pact have been Ruby microservices, and I have done what I suggested to do - use Pact for unit tests, and use something else for the "top-to-bottom" tests of the service.
We haven't. It hasn't been an issue for us, because the services have been so small, they only had one codepath, and all those codepaths went through the Client class (responsible for making the HTTP calls to the provider) and all the requests that client could make were tested with Pact in unit tests. I don't think this will work for the Javascript integration tests.
The fixtures are not necessarily shared JSON fixtures. You could have a shared instance of a model, where one test checks that the ProviderClient class returns some json that can be deserialised to that model, and another test uses that same model to stub the response from the ProviderClient class. This works fine in Ruby, but it's obviously not going to work for Javascript integration tests. I have recently added a feature to Pact which was missing before, which I think will make me change my position on using Pact at the "top-to-bottom" level of testing - the ability to use a Pact::Term in the path. This means it can be used in integration tests, allowing multiple requests to the same endpoint with different actual IDs without bloating the number of interactions that would need to be verified. You are right that using a shared JSON fixture can be brittle, but this approach would let you use the same fixture in a unit test with exact matching, and then use it in integrated tests with a
Yes. |
I say the term and something_like is definitely needed. Not sure I'm a fan of the 'something_like' function name, but still, just a wait to validate a json schema (or XML if you're so inclined) is something that would solve many (if not all) of the contract issues we have in our project. |
SomethingLike was never meant to be public! It was a hack that we added that actually turned out to be super useful. @BenSayers , what are your thoughts on this? https://gist.github.com/bethesque/94df51376c11aeeb6b71 I do not believe that JSON schema would solve our problems. Please see https://github.com/realestate-com-au/pact/wiki/FAQ#why-doesnt-pact-use-json-schema |
That's what I mean by json schema. I don't care about values, I just want to know that certain keys are required, some are optional, and all of them have a type (array, object, string, number) and the ability to be hierarchical. I don't care about the actual values themselves, just the structure. |
Yup, that's the "something like" matching. That's why it became so useful! |
I'm replying to @BenSayers comment on https://gist.github.com/bethesque/94df51376c11aeeb6b71 because gists do not have notifications for comments. (I just happen to have set up my own notifications script, otherwise I'd never have seen it).
I'd like this to be the central place to talk about this issue, as there are proper notifications.
Yes, it could work that way. I still don't know if this will work, it's just an idea I'm kicking around.
The fussy matching would be the current, exact matching. The flexible matching would just expect there to be a string. It makes sense in our brains that the response should correspond to the request, however, for the purposes of testing a UI, what we actually care about is that our UI displays what came back from the server, not that the response data matches the request data we sent. That assertion is actually the responsibility of the functional test in the provider.
You could specify the path in the
for all paths matching /things/:id, or for specific ids:
I think you might have it the wrong way around. I prefer the mock server to use "lax matching" for integration tests, because otherwise the tests are very brittle and hard to maintain. I've been burnt by that. I prefer the unit tests to use the fussy exact matching, because they are much easier to maintain, and can be very focussed. I do not know if this idea would work at all, it might just be better to have two completely separate sets of interactions, the specific unit test ones, and the flexible integration tests ones. |
A new thread for a conversation started here: DiUS/pact-consumer-js-dsl#27 (comment)
The text was updated successfully, but these errors were encountered: