4 ms·
> If your code depends on an external service, should your integration test actually make a (test) request to the real live service endpoints? That's a terribl
by WalterSear 9y ago
> If your code depends on an external service, should your integration test actually make a (test) request to the real live service endpoints?
That's a terrible idea. You've now coupled your tests to someone else's code.
Test the endpoints separately. Wrap the endpoints to allow you convenient dependency injection. Test the wrapper. This wrapper is ideally a very thin layer of boilerplate, that you won't ever touch very much, and shouldn't take much time to write or test.
But this is the key, however: use the data specifying the expected outputs of your third party API as input parameters to the test of the wrapper, and expected outputs from the wrapper as parameters for your integration tests. Keep each set of data in one place, so that changing it for one test, changes it for the others.
By using shared test datasets in this way, you prevent modules from getting out of synch with each other, and can still replace/update parts in separation.
- crdoconnor 9y ago>That's a terrible idea. You've now coupled your tests to someone else's code. It's a terrible idea not to. If your code isn't coupled to someone else's code your code isn't doing anything useful. Moreover, you should test against the code your code is coupled to as realistically as possible, which ideally means testing against the real thing.
- WalterSear 9y ago>It's a terrible idea not to. If your code isn't coupled to someone else's code your code isn't doing anything useful. Paradoxically, you want your tests separate from the external inputs, because complete control over the inputs is required to systematically and programmatically hit every edge case with every pass of your test suite. You also need the modularity in your tests and code, so you can fix and replace 3rd party endpoints without being afraid of polluting your code's inputs, or having to rewrite your integration suite. This is where the data contracts come in: they make sure that each module of the system and its partners are speaking the same language, even after they have been replaced or updated to handle new 3rd party behaviours.
- cjalmeida 9y agoI usually have this concept of integration tests covering the interaction of modules you are in control. 3rd party services are tested using something I call "edge tests". This leads to few mocks, yet I know the expected behavior of the external services.
- skybrian 9y agoYou're missing a fundamental difference in philosophy. Why should we only care about bugs in our code? They aren't the only bugs our users care about. A browser bug (for example) may not be our fault, but it is our problem. Therefore, we need to run our tests in real browsers to make sure it actually works, and keep running those tests on new browser releases to find any new bugs. Similarly, mobile apps need to be tested with a variety of different phones. If you upgrade a dependency and it changes behavior, you're still responsible for fixing or working around it, hopefully before users find out. Lots of interesting bugs happen in the gaps between modules, especially when they're maintained by different teams. A good reason to test code in isolation is that it makes diagnostics easier. However, if you only do that you're blind to a lot of other bugs.
- WalterSear 9y agoI beleive our disagreement is fundamentally one of semantics. However, I'll concur that I have not used this method for front end or mobile or embedded code yet, and there may be problems that I haven't anticipated. That said, I think you miss my point - I'm not saying don't test in a real-world environment: I'm saying decouple your program logic from its inputs, and enforce shared test data between top-level edge and the integration test suites to ensure end-to-end integrity (and do the same for every other part of your code that interacts with another part of your code, fwiw). > Test the endpoints separately. Wrap the endpoints to allow you convenient dependency injection. Test the wrapper. So, test in the browser, or on the app. Just test the endpoints separate from your integration test, and use the explicitly stated output of integration test as the input parameters for your browser tests. I'd go far as to argue that browsers and hardware - where you have the greatest concerns about the stability of your 3rd party APIs, are actually the circumstances where separating edge testing from integration makes the most sense. Perhaps a better way to explain my process is to think of my set of edge and integration tests as the equivalent of your 'integration' test suite, with the addition of specific contracts (the shared data) to ensure that interaction between modules is also controlled for. You are still testing end-to-end and you are still testing with real-world data, since that's what the edge tests will be returning. You are just explicitly stating what you expect at each boundary of the code.
- mcphage 9y ago> You've now coupled your tests to someone else's code. Your code is coupled to someone else's code. Which yeah, can lead to a lot of problems, but code that doesn't talk to someone else's code is usually not very useful.
- WalterSear 9y ago> You've now coupled your tests to someone else's code.
- jcrben 9y agoAnd so your tests actually demonstrate working code!
- deleted 9y ago[deleted]
- mcphage 9y agoRight, but what I'm saying is, it's even worse than that. Not only are your tests coupled to someone else's code, but your code itself is so coupled.
- WalterSear 9y agoThe trivial realities of the actual world may chill my heart to the bone, but even I can accept this sad inevitability ;) The thing is, the fact that your code relies on someone else's, and that their code can change out from under you is exactly why you need separation in your tests and modularity in your code. Using the system I am describing, you are still working with 'real world' data - you are just asserting it in advance (since the real world inputs correlated with the inputs of your integration suite via the edge tests) and ring fencing your endpoints. I've actually been part of a software team driven wild by unknown changes to infrastructure endpoints, causing failure of the integration test suite. And then by the brittleness of an integration suite too tightly coupled to inputs. I've also worked with test suites that failed when run after business hours, but only during Atlantic DST, for the same reasons. :|