It was a struggle. Like this corgi, I feel like it was a lot of effort without a lot of forward progression.
The reason for this was a monstrous test suite for our latest project. This project had us working in React again, this time making API calls to the Star Wars database (http://swapi.co/). An API call is basically telling your computer to go,Ā āHey, Star Wars database, I want this information!ā And then the server is like,Ā āOh, I have this info. Cool, here you go.ā And then you can work through that data and display it on the screen.
ps Why Star Wars? 1. Itās fun, and 2. Itās a flipping MASSIVE database, and part of the project was challenging us to work through all that data and clean it so we ended up with a small subset of relevant data.
API calls bring us into the world of asynchronous code. Normally, when you write code, the computer reads it a line at a time (more or less), from top to bottom (more or less). One function runs, completes, and then the next function runs, completes, and then the next, and so on.
Thatās usually fine because the computer can rip through all that code so fast that you, looking at the website/app/whatever, donāt even notice - it seems like itās happening all at once.
But API calls are expensive: they take a long time. What you donāt want is for your user to click a button and then stare at a blank screen that doesnāt appear to be doing anything for any noticeable amount of time (and youād notice it quickly - we start to wonder if something went wrong in a matter of milliseconds).
So how do you make all those calls without the user noticing a lag?
My partner and I solved that by front-loading all our API calls. We got all of our data when the page loads, and created a cute loading screen for the user to look at until the page was ready to render. It was a gif of BB-8 rolling across Jakku along with the word āloadingā - delightful. And that loading page never lasted for longer than two seconds. Doesnāt seem like much, but without that loading graphic, itād feel like forever.
You can also solve API call lag by wrapping them in a promise - which is specific programming term with a specific meaning which I still donāt fully understand - and letting the rest of your code continue on its way until the promise is resolved. Aka, until you get back the data you were waiting for. That way, the rest of your page can finish loading, and other things can be happening while youāre waiting for that one expensive function to complete.
The API calls and asynchronous code are difficult to test.
To back up here - a testing suite is a set of files full of code that tests your app code (the code that makes up your app). It either 1. double checks that everything works the way you think it works, or 2. tells you how to write your app code. That second option is called Test Driven Development (TDD) - the idea is that you write a test outlining what you WANT your code to eventually do, and then you write code to make that test pass.
Anyway. Our API call resulted in a massive set of data that also required additional API calls - very hard to control, very hard to test. So the solution in this case it to make use of a tool called fetchMock. Itās specifically for testing suites, and letās you fake an API call. Basically, when your app code tries to run an actual API call, your fetchMock catches it, stops the call, and returns data (that YOU define) back to your app code. Your app code thinks that data is the real response from the server, and happily proceeds with the rest of the functions none the wiser.
We created a bunch of fake data to give our app so we could test everything. The point of giving it fake data is that you can TEST with it. When you know what the data contains, you can predict what your app will do with it.
In this case, if I give my app a bunch of data about Luke Skywalker, I can expect it to display data about Luke Skywalker.
My instructor Meeka came up with the greatest analogy ever to explain using fake data in your test suites:
Random analogy to help grok mocking/spying/stubbing:
So letās say you have a bunch of people at a party. And you are the police in an unmarked van outside of the party (the Jest test suite). Ā You are trying to catch a notorious drug kingpen named Natalie - by putting a bunch of spies in the party who are wearing wires to record a time that Notorious Drug Kingpen Natalie makes a drug deal.
In order to put wires on party members (functions in your React component) - you have to actually physically catch those party goers in your van and attach said wire to them. Actually, in this analogy, you kidnap the party goers and replace them with fake party goers wearing a wire.
Itās super easy to catch the people who you were already sending to the party from the unmarked van (the functions you pass into the component as props). You stop them immediately and replace them with spies.
But what if you need to put a wire on people that you donāt know who are ALREADY at the party? Thatās way harder.
That represents things like Fetch. Fetch is already āat the partyā because it is something you already have hard coded in your component file when you write it in.
A package like Fetch-Mock, in this analogy, would be like if you installed a drone copter in the building and trained it to find a certain party goer and kidnap them.
The code to do that is a little complicated, but you could write it yourself. Like you could have built a drone but someone already did, so you might as well just buy it from Walmart, you know?
This has been the latest episode of āWeird Analogies Due to Too Much Coffeeā
Also - to stretch this analogy even farther.
Why do you have to sometimes mock functions or other props that you arenāt actually using for the test?
Well, depending on how your code was written, sometimes those things are required for your to Component to work properly.
Like, if you are putting a wire on a spy going into the party - you still have to put pants on that secret operative even if the wire is just attached to their shirt. Cause otherwise people will remark loudly that this party goer has forgotten pants (an error).
I donāt know if that makes sense to you guys, but it cracked me up.
Jack and I wrestled with our test suite and fetchMock for, no exaggeration, about twenty hours, all told. And we had 4 classmates and 2 instructors look at our code and say,Ā āHuh. I donāt know. That should be working.ā We actually finished the functionality of our app by Tuesday morning, and spent THE REST OF THE WEEK trying to get our tests passing.
Woof. It turned out, our tests were perfectly fine. The fake data we were giving back to our app while running our tests was the problem. Woof.
However, we ended up getting good scores on our project, and I feel confident that I understand how fetchMock and asynchronous testing work.