 I'm Ria Deshoria, and I'm super excited about this. So just a brief intro about me. I am a software engineer at Quizlet. I'm based out of San Francisco. I'm originally from India. I am a community advocate. I run various communities back in SF related to Google Developer Groups, and Google Developer Cloud Groups, and Women Tech Maker Groups. I am also an aspiring travel blogger since January 2020, to be honest. And the reason behind that was because I traveled to so many states in the US itself last year that my phone, this year I was changing my phone, and my phone was full of like 10,000 photos, and I had to transfer everything. So I thought like maybe I can just become a travel blogger while I'm coding. And this goes without saying, I love to sleep. Like I can sleep anywhere, anytime. Here's a brief outline about today's talk. First I'm going to give a brief introduction about what Quizlet is. Why do we use hacklang, and what is hacklang? Then I'm going to talk about testing with mockery and the limitations of FB intercept, which is the main reason why we created Hammock. Hammock is a mocking library which we recently open sourced, and then I'm going to talk about pitfalls of mocking. So let's get started with what Quizlet actually is and why do we use hacklang at Quizlet. Before I even say what Quizlet is for me, I'm going to share about what people think Quizlet is. Quizlet is a God, Quizlet is heaven for me. It's a way to save trees. It's also the reason why we should save net neutrality. It's also the reason for some people's graduation. Quizlet is blue, of course, because the color theme of Quizlet is blue. And more than that, Quizlet is the learning tool. Learning, Quizlet is a learning platform for anyone who wants to learn new things, be it teachers, students, or any other learners. The content on Quizlet is user generated. That means users can go and create their own study sets. They can use it for language learning, test, competitive exams, medical exams, and whatnot. Then, once they have their own sets created, then they can use, can I get a power charger? Power, yeah. Do you want one? I have. Okay, this is a technical conference and there were no difficulties setting my laptop up, so I was expecting something should at least go wrong. Back and up. Cool. So, I was talking about Quizlet. Quizlet can be used. There are, like, once students, after students create their own study sets, they can use various study modes like test, learn, write, spell, and flashcards to continue with their learning process. At this point, there are 50 million users on Quizlet using it on a monthly basis. And there are two million users who are signing up every month on Quizlet. There are 300 million study sets created on Quizlet so far and these sets can have as many terms as 2,000. And because, like, when the students are studying, there's a long history of study, that means our database is really huge. Quizlet's mission is to help students learn, which means that website needs to be fast and reliable. Initially, when Quizlet was started back in 2006, it was based on LAMP stack, PHP, Apache, MySQL, Linux, and PHP templates were used for the front end. Moving forward, as things were changing in technology, in 2011, we started using, we started the adoption of MVC framework. And as more and more students started using Quizlet, we realized that we need to make sure that Quizlet is more fast. Back in 2016, we started migration of PHP code and everything in the front end to React and Hacklang. This made sure that our backend performance is super great. Now, let me give you a brief about what Hacklang actually is before talking about why we created Hammock and more. So Hack is a dynamic programming language with the benefits of static type checking while adding many features which are commonly found in other modern programming languages. There are many new features in Hack which are not in PHP. Similarly, there are many features in PHP which Hack does not support. Hack is basically designed to, Hack is designed to make sure that PHP's disadvantages are no longer there. Hack currently runs on HHVM only. HHVM is a just-in-time compiler that will turn frequently running code into machine language and run that instead of interpreting it every single time. PHP can run on Hack for up to versions HHVM 3.30. Beyond that, PHP is not supported. However, PHP can run on any compiler via PHP net. Hack and HHVM is interoperable up to HHVM 3.0 and it is fully compatible with PHP. What that means is any code that is written in PHP can be called into Hack and similarly any code that is written into Hack can be called into PHP. There is a Hack type checker to analyze Hack code while you're editing the files. This helps prevent many runtime errors. One of the main reasons for us to use Hacklang was its type checking functionality. There are type annotations provided by Hacklang. You can use different formats like strict or partial. It helps you to find issues sooner than later. Here you can see an example. In the first line, when I'm declaring the code on the left hand is the Hack code, on the right hand is the PHP code. Just like any other programming languages, you can define that the variable count is an integer. For Hacklang, you need to specify it, especially if it's a strict mode. Similarly for functions, you need to define the type of arguments as well as the value the function is returning. But in PHP, you don't need to specify those things. The other main advantage for using Hack was XHP. XHP allows you to represent HTML tree as PHP and Hack object by using embedded HTML like syntax. In this example, you can simply use the HTML and use it directly. Whereas in the other page on the right side where we are using PHP, there are double codes and then you need to use the dollar username variable. Using XHP allows UI code to be type checked as well as it avoids several common issues such as cross-site scripting and double escaping. It also applies other validation rules. In PHP, when you are writing your code, it will never complain if head must contain title or not. Whereas in Hacklang, it will complain. The type checker will complain saying that oh, the XHP code is not valid because head is missing a title tag. There are other features which are available in Hacklang but not in PHP. Hack also has async functions. While Hack does not support multi-threading but multi-tasking is provided via async functions. You can run the second query while the first query is still in progress. This pretty much runs as smooth as synchronous functions. Hack also has seven types of collections which are vector, map, set, pair, immutable vectors, immutable map and immutable sets. Hack is designed to exclude features that are legacy, obscure or the features which hurts performances and that includes global statements, references and insensitive name lookup. Now, okay, now we have Hack code. Our entire code base is right now in Hacklang. Why do we use, we all need to do some sort of testing and so we started using mock rate to test our backend code. Now let's talk about what mock rate is and why we can't use that for long time. Mock rate is a PHP mocking library based on the list code substitution principle. It derives a mock class from a real class. While the mock class supports all the interfaces of the real class, it expects the mock object will have expectations and it will have throwing behavior on unexpected calls. If you see in this example, on the first line we are mocking a doc class. Dog is our mock class object and then it is using expectations such as should receive fetch is a method with passing the arguments and what it should return. We found that this often results in an overly specific test. The syntax is fluent but it is also verbose. There are many other disadvantages of using mock rate. If the mock rate uses class reflection, it will use code generation and evaluation to create mock classes. This hurts our performance of the test very badly and it doesn't work well with higher versions of HHVM. If you are on hack length, you need to upgrade to HHVM versions every now and then and mock rate is not very great with that. So what is the reason behind mock rate not working fine and what's the reason behind such shortcomings? Because when they were developing mock rate, there was no concept of FV intercept. FV intercept basically intercepts a function call and returns, invokes a user handler in case of function. In this example, there is a function called mock me which prints mock me. Then there is a handler function which takes n number of arguments and prints boohoo. If while intercepting, when you are intercepting mock me method and calling a handler function by using FV intercept, and then when you call the mocked function, it will return the handler. In this case, when you're intercepting mock me and then you're calling mock me, it will call handler function and print boohoo instead of mock me. While, so what we did was we used the FV intercept functionality and created a simple mocking utility based on that. We replaced some of our highest, some of our slowest mock recalls which were taking long time to run. We replaced that with this utility. That resulted into cutting down our tests through run time to half, which was like super great performance improvement. That's like every developer's dream. If the tests are taking less time to run, that's like wow, right? And so there was an option. Maybe we could have just stopped right there because 50% performance improvement itself is very great. But the thing is FV intercept is a very sharp knife. What that means is you can either misuse it or you can hurt yourself using it. And here's the reason. There are several limitations of FV intercept. First, it won't complain if you're intercepting a non-existent functions. There are hundreds of times when I would rename a variable or when I'm writing a variable, I would just flip the words, flip the characters in that and it would like make no sense at all. We all, you have like typing mistakes or like there's a lot of room for human error. In this case, when I'm intercepting, I am spelled correctly. Notice the spelling. And then I'm calling a handler function. It won't show any error. Even though if this function does not exist, the FV intercept will not complain. And then we will keep wondering, oh, I mocked the function correctly. Why am I not seeing any errors? This makes no sense and we'll like throw it on a laptop, sure. Second problem is it won't complain if you intercept an already intercepted function. Here on the first line, you're intercepting a function called getUser and making it call makeAlice. Cool. So now in your test, whenever you're calling getUser function, it will return makeAlice. Again, somehow you don't realize and you mock it again. And at that time, when you mock getUser, you're calling the handler function called makeBob. Well, tests are unhappy at this point because getUser will no longer return Alice. You can mock the same function as many times as possible. This is one of the disadvantage. And then you'll be like, well, I mocked it once, maybe twice, I don't remember, and so mocked, it's not returning the right values, anyhow. The third pitfall is the interception lasts forever unless manually undone. In case if you're intercepting a function, it is for forever. I am a developer, I'm writing tests, I intercepted a function called getUser and return makeAlice, and now I'm calling getUser, it's working fine. My colleague does the same thing, he just calls getUser, and because this function was intercepted in another test, it will still return makeAlice instead of the original values it should return. It will be an infinite time, it will be for forever, and it kind of leads to confusing behavior because once you're intercepted, you need to manually remove it unless otherwise it won't work. So because of all these shortcomings of mockery and because of all the pitfalls of FB intercept, we created another testing library, another mocking library called hammock. We recently open sourced it in December 2019, it's a mocking library for Hacklang, and it supports all the versions of HHVM at this point for Hacklang. However, if your PHP code is running on HHVM 3.30 or any lower versions, you can still use this mocking library. It helps write unit tests that are more type safe, performant, and flexible. Some of the features of hammock includes disposable interface for block levels mocking. What this means is whenever you're creating a mock, when you're creating a mock, we are calling FB intercept under the hood, and at the end of the life cycle of that mock, we are deactivating and deleting all the mocks. So in this case, other test results won't be affected. Like if you're using FB intercept directly and if you are not removing it, if you are not unlocking it, there's a great chance that other tests will be affected. However, if you're using hammock, no other test results will be affected. Other benefit is spying on functions without altering their behavior. Especially if you want to measure a side effect, like if you want to see if the cache is getting called or not, without altering the behavior of the cache, you can use hammock for that. It also allows you to mock an individual object without affecting any other object. You can create multiple objects of a class and mock them, but their mocking capabilities will be completely different. Hammock can also be used to track calls into the mocked functions. Most of the times you are wondering, like if a specific function is getting called or not, so you can assert on how many times a function was called using mock rate. First thing is disposable function mock. Mocks are usually useful when you are trying to overwrite a function's behavior with the stuff, as well as track calls into an overridden functions. Disposable ensures that all the mocks are destroyed at the end of the block in which they are created. And when the mock is destroyed, the original function, the original behavior of the mocked function is returned. In this case, you are creating just an object of the class dog, and then using mock class method to mock that object inside that block when you're calling dog and when you're calling fetch function on the dog object, and passing an argument called ball, it will return Frisbee. The thing, the reason is because inside, when you are using hammock to mock class method, the second argument you are passing is the function name, which is fetch, and then you are saying the arguments is, whenever fetch is called, return Frisbee. Doesn't matter what arguments you are passing into fetch, return Frisbee. So inside the block, inside the block when you're calling fetch method on dog, it's returning Frisbee. And then you can also use get num calls to find out how many times the mock was called or how many times the function was called using that mock. As soon as you get out of that block and you're calling fetch function on the dog object again, passing a different argument called ball, it will return ball. And the reason is because after outside the scope of the block, the mocks are destroyed. Other functionality is to mock individual objects. Hamock allows mocking individual objects without altering the behavior of other objects. In this case, first object which you have created is Alice and the other object is Bob. Now using mock object method, when you're mocking a fetch function on Alice object and passing an argument called Frisbee, which technically the function should return, in this block, when you are calling Alice object, when you're calling fetch method on Alice and passing an argument called ball, it will return Frisbee. The reason is because we mock that method, we mock that specific method only. In the same block, if you are calling fetch method on Bob object and pass a ball, as the argument it will still return ball because Bob as an object was never, it was never mocked. Again, outside the scope of this method, outside the scope of this, if you're calling fetch on Alice, it will again return ball because mocks are destroyed right after the scope. Spies are useful when you're trying to track the cause into a function without affecting its behavior. It's kind of like a special case of mock in which you can just pass through the original behavior. There are different kinds of spies like object method spy, class method spy, global function spy. When you are using spy class method, in this example, you are spying cache class. So under the scope of this method, when you are calling getByUsername, you are expecting that when you're calling getByUsername, it should go to cache first. If there is no cache, then only it should go to database. Then you're calling getNumCalls to find out if that specific class was ever called or not. This is the beautiful use of spies. Now, even though like hammock, this is a comparison with mockery. On the left side is the mockery example and on the right side is the hammock example. In mockery, in mockery, it uses class... Hammock focuses solely on mocking and so it tends to leave a smaller footprint than mockery. Instantiating a mock class in the mockery when it's mocking the doc class, it's mocking a class and not an object and then it is using expectations to figure out stuff and then you can call the same function. Whereas in mockery, it only focuses on mocking, it does not cares about expectations at all. And the thing is because mockery uses class reflection code generation, it takes a lot of time. So now that mocking is easier and more performant, the instinct might be to mock all the things, right? It's working fine, it's working super fast, so maybe we should just mock all the things. But as we know, with great power comes great responsibility. And that's why I also want to share about there are some pitfalls of mocking. There are things that you should be mindful of while you are mocking. Even though Hammock makes it super easy to mock anything, anywhere, but that doesn't mean you should. The less you mock, the less you mock, the more tests will be closer to their true execution. Mocking the internals of the system is never recommended, it is very dangerous to do that. However, it's okay to mock between system boundaries like networks and disk. There are some wisdoms for mocking. Good architectures are inherently testable. If it's a good architecture, you should not have to use much mocking at all. And mock sparingly, use as less mocks as possible. With this, I want to highlight some people. Thay is the one behind the idea of this testing library. He's the one who worked on it for a really long time. Here are some more people from Kizret who helped making this into reality and helped making this an open source. After we made open source, Lexeter has been really helping out us with moving. Initially, our Hammock was supporting only the version 3.25. Lexeter helped us move from HHVM 3.25 to all the versions of HHVM. And for us, this is a really great open source contribution. With this, I would like to wrap up my talk. Thank you so much. You can go to, you can check out this link or scan this barcode to go to Hammock Library on Kizret, Hammock Library on GitHub. Thank you.