<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Text on Nicole van der Hoeven</title><link>https://nicolevanderhoeven.com/tags/text/</link><description>Recent content in Text on Nicole van der Hoeven</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>Nicole van der Hoeven</copyright><lastBuildDate>Mon, 03 Apr 2023 13:20:53 +0100</lastBuildDate><atom:link href="https://nicolevanderhoeven.com/tags/text/index.xml" rel="self" type="application/rss+xml"/><item><title>Frontend vs. backend: How to plan your performance testing strategy</title><link>https://nicolevanderhoeven.com/blog/20230403-frontend-vs-backend-testing/</link><pubDate>Mon, 03 Apr 2023 13:20:53 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20230403-frontend-vs-backend-testing/</guid><description>&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://grafana.com/blog/2023/04/03/frontend-vs.-backend-how-to-plan-your-performance-testing-strategy/"&gt;here&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There are many aspects of application performance, but they broadly fall into two categories: frontend performance and backend performance. As a tester, it’s important to know the differences between the two and how that impacts the way you approach your tests.&lt;/p&gt;
&lt;p&gt;In this blog, I’ll provide a high-level overview of frontend performance testing and backend performance testing, including pros and cons of each one. I’ll also explain why you need both if you really want a holistic view of how your application is performing.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/xVACRP5qIJI?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="why-and-where-to-use-frontend-performance-testing"&gt;Why (and where) to use frontend performance testing&lt;/h2&gt;
&lt;p&gt;Frontend performance testing verifies user experience &lt;a href="https://grafana.com/blog/2023/02/10/watch-how-to-pair-grafana-faro-and-grafana-k6-for-frontend-observability/"&gt;at the interface level&lt;/a&gt;. Basically, this entails everything a user sees when they access your web app, whether it’s on a laptop or other devices. This might involve automating user interactions with the site through the browser — for example, clicking on a button, filling out a form, or having scripts that load on the browser and run.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/crocs-r-cool.png" alt="mockup of a site called “Crocs R Cool”" loading="lazy" decoding="async"&gt;
&lt;em&gt;In frontend testing, you&amp;rsquo;re looking at how components of the UI respond.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The metrics we analyze for frontend performance tests are typically quite different from what we use for backend testing. Let’s say we want to know the time it takes for a page to render after a user clicks on a button that directs them there. This isn’t the same thing as measuring server response time; that total round trip takes a lot more into account — things like latency or the time it takes for a client-side script to run and display the requested information.&lt;/p&gt;
&lt;h3 id="advantages-of-frontend-performance-testing"&gt;Advantages of frontend performance testing&lt;/h3&gt;
&lt;p&gt;The great thing about frontend performance testing is that it provides a more realistic measurement of your end users’ experiences. They don’t care what’s going on under the hood; they care about the overall experience — and that’s exactly what frontend performance testing measures. Unlike backend performance testing, frontend performance testing looks at everything from a user’s point of view: from the time they visit a webpage to the time they can interact with something on that page.&lt;/p&gt;
&lt;p&gt;On top of that, most (about 80%) of the performance issues end users report are frontend issues. It doesn’t make much sense to skip this type of testing; it can be a critical step in finding ways to improve the overall user experience.&lt;/p&gt;
&lt;h3 id="disadvantages-of-frontend-performance-testing"&gt;Disadvantages of frontend performance testing&lt;/h3&gt;
&lt;p&gt;The most obvious disadvantage of frontend testing is that it doesn’t look under the hood. If you only do frontend testing, you’re almost certain to miss something. Maybe a component isn’t working, which is creating issues for your users. But you wouldn’t know that because you’re only testing what’s visible from the browser. In other words, you might be in the situation where you know something in the application is wrong and responding too slowly, but you don’t know what.&lt;/p&gt;
&lt;p&gt;Frontend performance testing can also be quite expensive compared to, say, protocol-based testing. That’s because you need enough resources (memory and CPU) to actually spin up a browser instance and interact with your application the way that a real user would. The same load generator that could run tens of thousands of backend virtual users may only be able to run 50 frontend virtual users before running out of resources.&lt;/p&gt;
&lt;p&gt;Finally, only testing frontend performance can sometimes lead to pretty misleading results, especially as you scale. As load increases, the response time for the frontend remains fairly constant, but that’s not the case with the backend.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/Frontend-Backend-LoadTesting.png" alt="graph plotting response time over the number of concurrent users for front-end and back-end" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;While the frontend component of response time remains more or less constant, the backend component of response time increases exponentially with the number of concurrent users.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This phenomenon occurs because most of the issues that frontend performance tests might identify, such as a slow-running client-side script, don’t tend to compound. It’ll be just as slow on each user’s computer regardless of how many users are running it. However, problems uncovered by backend performance testing are on the side of the application, not the users. If a database can’t keep up with 100 users sending queries to it, then 1,000 users sending it queries is likely to yield an even worse response time.&lt;/p&gt;
&lt;h2 id="why-and-where-to-use-backend-performance-testing"&gt;Why (and where) to use backend performance testing&lt;/h2&gt;
&lt;p&gt;As you might have guessed at this point, backend performance testing targets the infrastructure under the hood. This could be a database that contains customer information, a payment gateway, or an authentication service for customers that have already signed up. These are things that an end user doesn’t see and maybe isn’t ever even aware of, but they still have an effect on response times.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/backend.png" alt="graphic of application components" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Backend testing addresses infrastructure users might not even be aware of, like different databases, gateways, and services that underpin an application.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Scripting against the backend is quite different from scripting against the frontend. Instead of scripting user interactions like clicking, you’re scripting certain requests that are being sent on the protocol level. So, for example, you could have an HTTP GET request for the page and also for its embedded resources.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6/http&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;https://test.k6.io&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The sample Grafana k6 script above is an example of a simplified backend performance test. It makes a single HTTP request to retrieve the contents of &lt;a href="https://test.k6.io"&gt;https://test.k6.io&lt;/a&gt;, and then waits one second.&lt;/p&gt;
&lt;p&gt;Backend performance includes things beyond just load times and latency. It includes scalability, availability, reliability, and similar attributes, so the metrics that we measure are different as well. We might look at server uptime or response times per component. We could also trace the path of a particular request through the application stack.&lt;/p&gt;
&lt;p&gt;An illustration shows that performance testing can address scalability, elasticity, availability, latency, resiliency, and reliability.
Application performance is more than just speed. Backend testing helps us verify many different aspects of performance.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/performance-ilities.png" alt="diagram of different types of performance" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Application performance is more than just speed. Backend testing helps us verify many different aspects of performance.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="advantages-of-backend-performance-testing"&gt;Advantages of backend performance testing&lt;/h3&gt;
&lt;p&gt;As we already discussed, frontend testing doesn’t cover everything, but it’s also more complicated than that. The 20% or so of performance bottlenecks that are backend-related can be the hardest to troubleshoot, identify, and fix. Even worse, a lot of backend performance issues can spiral out of control at higher levels of load and can be quite expensive to fix later on in a development cycle. The worst time to realize that you need to rethink your entire infrastructure is after it’s already failed in production. If you take care of backend performance testing earlier (commonly known as shifting left), it makes such pivots less costly.&lt;/p&gt;
&lt;p&gt;Another advantage of backend testing is the ability to target different components of an application. You can choose which API endpoint to hit and how hard, and you can target specific user flows, so it doesn’t always have to be end-to-end testing.&lt;/p&gt;
&lt;p&gt;And finally, backend performance tests tend to be less resource-intensive; they’re just protocol-level requests that can be easily ramped up.You don’t need to spin up a browser instance, or even parse through the Document Object Module (DOM) of a webpage or look for specific elements. It’s just a matter of sending these requests, and that’s something that you can increase the scale of pretty cheaply.&lt;/p&gt;
&lt;h3 id="disadvantages-of-backend-performance-testing"&gt;Disadvantages of backend performance testing&lt;/h3&gt;
&lt;p&gt;Backend performance testing won’t tell you anything about what’s happening on the frontend, and a lot of sites these days actually use scripts that have to run on the frontend. So with backend performance testing tools, you just download those scripts. But the response time they measure is the download time, which is not really realistic because those scripts are executed. A backend performance test would fetch the text of the script. A frontend performance test would run it — and that script might do something like perform a calculation. You need frontend performance testing to measure that properly.&lt;/p&gt;
&lt;p&gt;Backend performance testing is also much more expansive than front end performance testing, especially with microservices-based applications. There are so many components to track that it can be difficult to determine where to look. You can quickly find yourself going down a rabbit hole of constant, never-ending performance tuning.&lt;/p&gt;
&lt;h2 id="frontend--backend-testing-for-the-win"&gt;Frontend + backend testing for the win&lt;/h2&gt;
&lt;p&gt;So, as we’ve seen, frontend testing and backend testing both seem to have advantages and disadvantages. So which one should you use? Both, if at all possible! They look at different parts of application performance, so doing both is essential if you want a holistic view of user experience.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/frontend-vs-backend-meta.png" alt="diagram of k6 being used for both frontend and backend testing" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Software teams can use Grafana k6 to address frontend and backend performance testing.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you’re operating at a smaller scale, use your own discretion about how to find the right mix for needs. But if you’re testing at a larger scale with thousands of users and you want to test both the frontend and backend aspects of application performance, I suggest you change your test strategy so the majority of the load is generated with backend performance testing scripts. That’s because of the resource intensiveness of frontend performance testing scripts, which we discussed earlier.&lt;/p&gt;
&lt;p&gt;So, if you generate most of the load with the backend and then you have a handful of frontend performance testing scripts running in the background, you should still get a good idea of how both are performing. Remember, frontend application performance remains fairly constant throughout different levels of load, so you’ll still get a lot of useful insights on both types of performance without worrying too much about the scaling costs.&lt;/p&gt;
&lt;p&gt;Frontend and backend performance both affect what your end users think of your application. If the underlying application servers respond immediately to all requests, but then a huge picture of a crocodile makes your homepage excruciatingly slow to load, then users aren’t going to like that.&lt;/p&gt;
&lt;p&gt;On the other hand, if your homepage is really sleek and quick to load, but the application server hasn’t responded with the data that the browser needs to render, then users aren’t going to like that either.&lt;/p&gt;
&lt;p&gt;A common problem when testing both frontend and backend performance is script duplication: because of the differences in the two approaches, you often have to use two separate tools — which means two test suites and potentially two scripting languages. While this is a valid approach, it’s more sustainable to look for a tool that can target both types of performance, such as k6.&lt;/p&gt;
&lt;p&gt;Whichever tool(s) you end up using, testing both frontend and backend performance is going to give you the clearest view of how your application really performs.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The easiest way to get started with k6 is through Grafana Cloud k6. The free tier includes 500 virtual user hours/month for k6 testing and more. &lt;a href="https://grafana.com/auth/sign-up/create-user?pg=blog&amp;amp;plcmt=body-txt"&gt;Sign up for free now!&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Guide to Load Testing Websites</title><link>https://nicolevanderhoeven.com/blog/20230112-load-testing-websites/</link><pubDate>Thu, 12 Jan 2023 17:12:42 +0000</pubDate><guid>https://nicolevanderhoeven.com/blog/20230112-load-testing-websites/</guid><description>&lt;p&gt;&lt;em&gt;Original article &lt;a href="https://k6.io/docs/testing-guides/load-testing-websites/"&gt;here&lt;/a&gt;, written for k6.io&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This doc explains some key concepts about load testing websites, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The difference between backend and frontend performance testing&lt;/li&gt;
&lt;li&gt;When to choose between protocol-based, browser-based, or hybrid scripts&lt;/li&gt;
&lt;li&gt;Recommended practices about how to load test websites&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All load tests try to simulate real user traffic to prevent failures, improve reliability, and release new code with confidence.
But your approach to load testing must adapt to the type of application you want to test.&lt;/p&gt;
&lt;p&gt;In this guide, learn about strategies to test websites, including specific recommendations for complex scenario scripting and test execution.&lt;/p&gt;
&lt;h2 id="load-testing-approaches"&gt;Load testing approaches&lt;/h2&gt;
&lt;p&gt;When you approach the load test, first consider the following perspectives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Backend vs. frontend performance&lt;/li&gt;
&lt;li&gt;Protocol-based, browser-based, or hybrid load testing&lt;/li&gt;
&lt;li&gt;Component testing vs. end-to-end testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="backend-vs-frontend-performance"&gt;Backend vs. frontend performance&lt;/h2&gt;
&lt;p&gt;Performance has a significant influence on the user experience of a website.&lt;/p&gt;
&lt;p&gt;For example, when users want to visualize or interact with some information, they expect the website to react quickly.&lt;/p&gt;
&lt;p&gt;To measure performance, testers often look at response times, which are affected by two main factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontend performance&lt;/li&gt;
&lt;li&gt;Backend performance&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="frontend-performance"&gt;Frontend performance&lt;/h3&gt;
&lt;p&gt;Frontend performance testing verifies application performance on the interface level, measuring round-trip metrics that consider how and when page elements appear on the screen. It&amp;rsquo;s concerned with the end-user experience of an application, usually involving a browser.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/frontend-rendering.png" alt="Snapshots over time of webpage elements rendering on a browser" title="Snapshots over time of webpage elements rendering on a browser" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Frontend performance testing excels at identifying issues on a micro level but does not expose issues in the underlying architecture of a system.&lt;/p&gt;
&lt;p&gt;Because it primarily measures a single user&amp;rsquo;s experience of the system, frontend performance testing tends to be easier to run on a small scale.
Frontend performance testing has metrics that are distinct from backend performance testing. Frontend performance tests for things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Whether the pages of the application are optimized to render quickly on a user&amp;rsquo;s screen&lt;/li&gt;
&lt;li&gt;How long it takes a user to interact with the UI elements of the application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some concerns when doing this type of performance testing are its dependency on fully integrated environments and the cost of scaling. You can test frontend performance only once the application code and infrastructure have been integrated with a user interface. Tools to automate frontend testing are also inherently more resource-intensive, so they can be costly to run at scale and are not suitable for high load tests.&lt;/p&gt;
&lt;h3 id="backend-performance"&gt;Backend performance&lt;/h3&gt;
&lt;p&gt;Backend performance testing targets the underlying application servers to see how they behave under production-like conditions. When it comes to websites, while frontend performance involves how assets included in the page are rendered, backend performance focuses on how those assets are processed by the application servers, served to users, and downloaded by browsers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/backend-performance.png" alt="Backend components" title="Backend components of an application" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Backend testing is broader in scope than frontend performance testing. API testing can be used to target specific components or integrated components, meaning that application teams have more flexibility and higher chances of finding performance issues earlier. Backend testing is less resource-intensive than frontend performance testing and is thus more suitable for generating high load.&lt;/p&gt;
&lt;h3 id="which-one-should-you-test"&gt;Which one should you test?&lt;/h3&gt;
&lt;p&gt;It depends! Ideally, both.&lt;/p&gt;
&lt;p&gt;Frontend testing tools are executed on the client side and are limited in scope: they do not provide enough information about backend components for fine-tuning beyond the user interface.&lt;/p&gt;
&lt;p&gt;This limitation can lead to false confidence in overall application performance when the amount of traffic against an application increases. While the frontend component of response time remains more or less constant, the backend component of response time increases exponentially with the number of concurrent users:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/Frontend-Backend-LoadTesting.png" alt="Backend performance often contributes significantly to overall user experience" title="Backend performance often contributes significantly to overall user experience" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Testing &lt;em&gt;only&lt;/em&gt; frontend performance ignores a large part of the application, one more susceptible to increased failures and performance bottlenecks at higher levels of load.&lt;/p&gt;
&lt;p&gt;Testing &lt;em&gt;only&lt;/em&gt; backend performance, on the other hand, ignores &amp;ldquo;the first mile&amp;rdquo; of user experience and breadth. Backend testing involves messaging at the protocol level rather than interacting with page elements the way a real user would. It verifies the foundation of an application rather than the highest layer of it that a user ultimately sees.&lt;/p&gt;
&lt;p&gt;Testing both frontend and backend performance leads to the best overall performance and user experience for your application. Ignoring one or the other exposes you to performance bottlenecks that significantly decrease a user&amp;rsquo;s satisfaction with your application.&lt;/p&gt;
&lt;p&gt;However, if your testing is smaller in scope, you can choose to focus on either frontend or backend with the goal of eventually building a test suite that encompasses both.&lt;/p&gt;
&lt;h2 id="component-testing-vs-end-to-end-testing"&gt;Component testing vs. end-to-end testing&lt;/h2&gt;
&lt;p&gt;When you&amp;rsquo;re testing a web app, you may wonder whether how you structure your test scripts. Consider the following two methods.&lt;/p&gt;
&lt;h3 id="component-testing"&gt;Component testing&lt;/h3&gt;
&lt;p&gt;One way you could load test a web app is to load test its components. Maybe you know there are issues with a specific functionality due to previous production issues, or maybe you&amp;rsquo;d like to target components that are business-critical to reduce risk exposure.&lt;/p&gt;
&lt;p&gt;In these cases, the type of test you write may:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;be protocol-based&lt;/li&gt;
&lt;li&gt;call only those API endpoints that are relevant&lt;/li&gt;
&lt;li&gt;have smaller or no sleep/think time&lt;/li&gt;
&lt;li&gt;be focused on eventually stress testing a component or service or finding its breaking point&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Testing in this way is more flexible. With protocol testing, you can specify the endpoints or servers you&amp;rsquo;d like to hit, and narrow your test&amp;rsquo;s target that way. You can test specific functionalities while skipping others that come chronologically before it in a standard user flow. You can more finely control the type of traffic that is generated. If you&amp;rsquo;re recreating a specific mix of requests, you can script those requests and reproduce them in a repeatable manner.&lt;/p&gt;
&lt;p&gt;Doing component testing for load may not always require that your script behave like an end user. In fact, it may be necessary to artificially inflate traffic to more quickly reproduce issues, or to deflate traffic to reduce the noise in the logs. By the nature of this type of testing, scripts don&amp;rsquo;t contain the full flow of a request that you would expect to see in production, so realism is not a priority.&lt;/p&gt;
&lt;h3 id="end-to-end-testing"&gt;End-to-end testing&lt;/h3&gt;
&lt;p&gt;You could also do end-to-end testing against a web app. End-to-end testing seeks to replicate real user behaviour and track its effects across the entire stack. When doing end-to-end testing, you might:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;do protocol-level, browser-level, or hybrid load testing&lt;/li&gt;
&lt;li&gt;replicate the actions in a typical user flow&lt;/li&gt;
&lt;li&gt;think about the performance of the entire workflow as well as how long a request took to be processed by each component&lt;/li&gt;
&lt;li&gt;write scripts that access the application the same way users would, such as by browsing to the homepage before finding their way to other parts of the website&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This type of load testing is broader in scope than component testing, but shallower in terms of depth. With end-to-end testing, you get a better idea of the full user experience of the application as a whole. However, it can also be more complex to troubleshoot, as you have more components to monitor and more places to look for issues that are found.&lt;/p&gt;
&lt;p&gt;End-to-end testing is based on real user behavior, so it&amp;rsquo;s often important that end-to-end test scripts also be realistic.&lt;/p&gt;
&lt;h2 id="protocol-based-browser-based-or-hybrid-load-testing"&gt;Protocol-based, browser-based, or hybrid load testing&lt;/h2&gt;
&lt;p&gt;The decision of whether to test the frontend, backend, or both will also affect the type of load testing you should carry out and the kind of scripts you should write.&lt;/p&gt;
&lt;h3 id="protocol-based-load-testing"&gt;Protocol-based load testing&lt;/h3&gt;
&lt;p&gt;Protocol-based load testing verifies the backend performance of an application by simulating the requests underlying user actions. For websites, this commonly involves HTTP requests that bypass the user interface of your application and are sent directly to a server or application component.&lt;/p&gt;
&lt;p&gt;For example, a protocol-based load testing script might request all the resources on a webpage from the application servers, but those resources are merely downloaded. The response times reported by a purely protocol-based script do not include frontend metrics such as the time taken for images to render on a browser. The load is generated by simulating many requests sent to application servers.&lt;/p&gt;
&lt;p&gt;While protocol-based load testing may seem to lend itself better to component testing, you can also do end-to-end website testing with protocol-level scripts.&lt;/p&gt;
&lt;h4 id="sample-protocol-based-test-script"&gt;Sample protocol-based test script&lt;/h4&gt;
&lt;p&gt;The following is an example of a protocol-based load testing script in k6 that fetches the homepage, along with resources embedded into the page.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6/http&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;check&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Homepage&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;sec-ch-ua&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;Chromium&amp;#34;;v=&amp;#34;94&amp;#34;, &amp;#34;Google Chrome&amp;#34;;v=&amp;#34;94&amp;#34;, &amp;#34;;Not A Brand&amp;#34;;v=&amp;#34;99&amp;#34;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;accept-encoding&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;gzip, deflate, br&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;accept-language&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;en-GB,en;q=0.9&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 01. Go to the homepage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;responses&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;batch&lt;/span&gt;([
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://mywebsite.com/&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://mywebsite.com/style.min.css&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://website.com/header.png&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://website.com/polyfill.min.js&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;check&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;responses&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Homepage loaded&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;includes&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Welcome to my site&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 02. View products
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;responses&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;batch&lt;/span&gt;([
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://mywebsite.com/products&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://mywebsite.com/style.css&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://website.com/product1.jpg&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://website.com/product2.jpg&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;https://website.com/displaylist.js&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;check&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;responses&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Products loaded&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;includes&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Add to Cart&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://k6.io/docs/test-authoring/recording-a-session/browser-recorder/"&gt;Recording browser traffic&lt;/a&gt; helps you prototype to test websites on the protocol level.&lt;/p&gt;
&lt;h3 id="browser-based-load-testing"&gt;Browser-based load testing&lt;/h3&gt;
&lt;p&gt;Browser-based load testing verifies the frontend performance of an application by simulating real users using a browser to access your website.&lt;/p&gt;
&lt;p&gt;For example, a browser-based load testing script might include instructions to navigate to a page, click on a button, and type out text on a form. Those user actions then trigger underlying requests on the protocol layer, but only user actions are scripted in browser-based testing.&lt;/p&gt;
&lt;p&gt;Unlike protocol-based load testing, browser-based load testing scripts generate load by starting multiple instances of browsers and interacting with your application the way real users would. Testing at the browser level can also be the only option for testing Single-Page Applications where a lot of the application logic is executed by client-side scripts.&lt;/p&gt;
&lt;p&gt;Scripting on the browser level usually requires the use of different tools from the ones used to test at the protocol level.
However, k6 has an extension called &lt;a href="https://k6.io/docs/javascript-api/xk6-browser/"&gt;xk6-browser&lt;/a&gt; that allows the creation of browser-based test scripts alongside protocol-based ones.&lt;/p&gt;
&lt;h4 id="sample-browser-based-test-script"&gt;Sample browser-based test script&lt;/h4&gt;
&lt;p&gt;The following is an example of a browser-based load testing script in k6 using xk6-browser on a dummy website. Instead of making an HTTP request, the script views the homepage, then looks for and clicks on a link to the product page.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;chromium&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6/x/browser&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;chromium&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;launch&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;headless&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;newPage&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 01. Go to the homepage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;goto&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;https://mywebsite.com&amp;#39;&lt;/span&gt;, { &lt;span style="color:#a6e22e"&gt;waitUntil&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;networkidle&amp;#39;&lt;/span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;waitForSelector&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;p[class=&amp;#34;woocommerce-result-count&amp;#34;]&amp;#34;]&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;screenshot&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;screenshots/01_homepage.png&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 02. View products
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;a[class=&amp;#34;woocommerce-LoopProduct-link woocommerce-loop-product__link&amp;#34;]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;click&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;waitForSelector&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;button[name=&amp;#34;add-to-cart&amp;#34;]&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;screenshot&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;screenshots/02_view-product.png&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="tips-for-writing-browser-level-scripts"&gt;Tips for writing browser-level scripts&lt;/h4&gt;
&lt;p&gt;The following steps can help you get started with a browser-level test script.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Script user actions, not requests.&lt;/strong&gt; Determine what a user might do for a particular task, and script interactions with elements on the browser level. For example, script what buttons the user clicks on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Identify unique selectors.&lt;/strong&gt; Once you have identified which page elements a user interacts with, use the Element Inspector for DevTools in your browser to find a unique, static, and simple way to identify each element. The script needs selectors to find the right element to interact with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use elements to verify responses.&lt;/strong&gt; After every action, use &lt;a href="https://k6.io/docs/javascript-api/xk6-browser/api/locator/"&gt;locators&lt;/a&gt; to search for elements on the page that you would expect to find. This verification helps ensure that the script has reached the expected page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Take screenshots for every action while debugging.&lt;/strong&gt; One of the advantages of browser-based testing is the ability to take screenshots. After every user interaction the script simulates, use &lt;a href="https://k6.io/docs/javascript-api/xk6-browser/api/page/"&gt;page.screenshot&lt;/a&gt; to save a visual image of what the script encountered for later troubleshooting.&lt;/p&gt;
&lt;h3 id="hybrid-load-testing"&gt;Hybrid load testing&lt;/h3&gt;
&lt;p&gt;Hybrid load testing is a combination of protocol-based and browser-based load testing. While you can use two tools or two scripts to execute different types of load tests (one protocol-based and one browser-based), having both types of tests executed by the same script and the same tool is ideal. Aggregating results between different tools can be difficult at best and inconsistent at worst.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/hybrid-testing.png" alt="Hybrid of frontend and backend performance testing" title="In hybrid load testing, both the frontend and backend are tested" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;A best practice in hybrid load testing is to generate most of the load using the protocol-level test, and then to run a smaller number of the browser-level testing scripts. This approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reduces the number of load generators needed, since protocol-level testing requires fewer machines to generate the same load&lt;/li&gt;
&lt;li&gt;measures backend and frontend performance in the same test execution&lt;/li&gt;
&lt;li&gt;provides a single source of aggregated output at the end&lt;/li&gt;
&lt;li&gt;reduces complexity in the creation and maintenance of scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="scripting-considerations"&gt;Scripting considerations&lt;/h2&gt;
&lt;p&gt;When you script a test for a website, consider these recommendations.&lt;/p&gt;
&lt;h3 id="consider-factors-that-affect-script-realism"&gt;Consider factors that affect script realism&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Record your user journey.&lt;/strong&gt; Using the browser recorder can facilitate initial test script creation by capturing all the embedded resources on webpages. Check out the &lt;a href="https://k6.io/docs/test-authoring/recording-a-session"&gt;Session Recording guide&lt;/a&gt; to learn more about how to auto-generate your load test from a user session.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Correlate data.&lt;/strong&gt; Recordings often don&amp;rsquo;t take into account dynamic values that are generated anew each time a request is made. Go through the recorded requests and determine whether you need to &lt;a href="https://k6.io/docs/examples/correlation-and-dynamic-data/"&gt;extract values from previous responses&lt;/a&gt; and use parameters in subsequent requests. This practice ensures your VUs behave more like real users would.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Include or exclude static resources.&lt;/strong&gt; Determine whether you should include or exclude static resources on pages such as images, JavaScript, etc. Consider including them if you want to measure overall user experience. Consider excluding them if you are using a Content Delivery Network (CDN) that is under a separate Service Level Agreement (SLA).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exclude third-party requests.&lt;/strong&gt; Don&amp;rsquo;t load test servers that you don&amp;rsquo;t own. Many applications make calls to third-party providers for authentication, social sharing, and marketing analytics. Disable these requests unless you have permission to include them in your tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use concurrent requests.&lt;/strong&gt; To mimic the way modern browsers download some requests in parallel, use &lt;a href="https://k6.io/docs/javascript-api/k6-http/batch/"&gt;batching&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Determine cache and cookie behaviour.&lt;/strong&gt; k6 automatically resets cookies between iterations, but you can also &lt;a href="https://k6.io/docs/using-k6/k6-options/reference/#no-cookies-reset"&gt;change this behavior&lt;/a&gt; if maintaining cookies would be more realistic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use dynamic think time and pacing.&lt;/strong&gt; Consider adding varying &lt;a href="https://k6.io/docs/javascript-api/k6/sleep/"&gt;delays&lt;/a&gt; so you don&amp;rsquo;t artificially stagger a script with completely uniform delays.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use test data.&lt;/strong&gt; Real users typically don&amp;rsquo;t search for or submit the same data repeatedly. Consider adding a &lt;a href="https://k6.io/docs/examples/data-parameterization/"&gt;test data file&lt;/a&gt; for the script to iterate through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Model test parameters and load profile after production.&lt;/strong&gt; In k6, you can use &lt;a href="https://k6.io/docs/using-k6/k6-options/how-to"&gt;test options&lt;/a&gt; to determine the exact shape and profile of your load test script. Select the appropriate &lt;a href="https://k6.io/docs/using-k6/scenarios/executors/"&gt;executors&lt;/a&gt; for the job.&lt;/p&gt;
&lt;h3 id="create-a-reusable-framework"&gt;Create a reusable framework&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Use tags and groups.&lt;/strong&gt; Organizing requests by &lt;a href="https://k6.io/docs/using-k6/tags-and-groups/"&gt;tagging and grouping them&lt;/a&gt; helps you consolidate like metrics and makes your test scripts more understandable by others.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use scenarios.&lt;/strong&gt; When combining protocol-based and browser-based tests, use &lt;a href="https://k6.io/docs/using-k6/scenarios/"&gt;scenarios&lt;/a&gt; to independently control their test parameters and &lt;a href="https://k6.io/docs/using-k6/scenarios/executors/"&gt;executors&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Modularize scripts.&lt;/strong&gt; Use &lt;a href="https://k6.io/docs/using-k6/modules/"&gt;modules&lt;/a&gt; to separate and organize functions for protocol-level testing and browser-level testing, and then use a test runner script to execute them. This approach means different scripts can be versioned and changed without affecting each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Integrate your tests into your CI pipeline.&lt;/strong&gt; Adopting a &amp;ldquo;tests as code&amp;rdquo; approach enables you to tie your load tests more closely into your project&amp;rsquo;s existing CI/CD processes, helping you get the most value out of every test.&lt;/p&gt;
&lt;h3 id="test-with-thresholds-in-mind"&gt;Test with thresholds in mind&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Create thresholds for both types of testing.&lt;/strong&gt; Some browser-level and protocol-level metrics cannot be combined because they don&amp;rsquo;t measure the same thing. Set &lt;a href="https://k6.io/docs/using-k6/thresholds/"&gt;thresholds&lt;/a&gt; for relevant metrics from both the browser-level script and the protocol-level one.&lt;/p&gt;
&lt;h3 id="when-possible-use-hybrid-load-testing"&gt;When possible, use hybrid load testing&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Use protocol-based scripts to generate majority of the load.&lt;/strong&gt; When writing the test scenario, use the protocol-based requests to simulate most of the traffic and use fewer VUs for the browser-based requests. Relying on the protocol-level traffic helps keep resource utilization on load generators down.&lt;/p&gt;
&lt;h2 id="execution-considerations"&gt;Execution considerations&lt;/h2&gt;
&lt;p&gt;When you run tests, consider the test environment and the load generator location.&lt;/p&gt;
&lt;h3 id="run-your-tests-in-the-appropriate-environment"&gt;Run your tests in the appropriate environment&lt;/h3&gt;
&lt;p&gt;Testing in pre-production environments and in production environments both add value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testing in pre-production environments&lt;/strong&gt; (staging, test, system integration testing, user acceptance testing, and production replica environments) enables you to identify performance defects early, which can save a lot of time and effort (and reputation) later on. Running in test environments also means you can often afford to be more aggressive with your tests. However, it&amp;rsquo;s also more critical to get your load profiles right, and the test results you get may not necessarily apply to production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testing in production&lt;/strong&gt; yields the most accurate results, but it&amp;rsquo;s also more risky. Often, testing production is the only feasible alternative. You can reduce the risk of impact to real customers while testing in production by using lower levels of load when running load tests during peak hours, schedule tests for off-peak hours, choosing load test types that are less risky, using techniques like synthetic monitoring that generate less traffic, using real user monitoring tools to get snapshots of user performance at load, and ensuring that your observability stack is working at peak efficiency.&lt;/p&gt;
&lt;h3 id="run-tests-where-your-customers-are"&gt;Run tests where your customers are&lt;/h3&gt;
&lt;p&gt;The location of the load generator(s), where the traffic is coming &lt;em&gt;from&lt;/em&gt;, can also have an impact on your test results. The question is: where are your end users located?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On-premises load testing&lt;/strong&gt; can be ideal when testing early on in the development a website, or when there are machines that can be repurposed as load generators. However, testing entirely from within a corporate network may also yield false positives, in that response times reported are significantly lower than if the same application servers were accessed from across the country.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Load testing on the cloud&lt;/strong&gt; is an essential part of the testing strategy for many public-facing websites. Using load generators on the cloud gives you access to test in different states and geographical countries, creating a mix of load generators proportional to your users&amp;rsquo; locations. Cloud load generators are easier to provision and cheaper to maintain in the long run than on-premise ones. Load testing on the cloud can help you include the effects of network latency in your tests and yield results that are more realistic.&lt;/p&gt;
&lt;h2 id="recommendations"&gt;Recommendations&lt;/h2&gt;
&lt;p&gt;Here are some recommendations to help you plan, script, and execute your load tests for websites.&lt;/p&gt;
&lt;p&gt;If you want to test the last-mile user experience of your website:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;focus on &lt;strong&gt;frontend performance&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;write &lt;strong&gt;browser-based test scripts&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;and consider doing more realistic &lt;strong&gt;end-to-end&lt;/strong&gt; tests of the user flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to test the underlying infrastructure of your website:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;focus on &lt;strong&gt;backend performance&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;write &lt;strong&gt;protocol-based test scripts&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;and consider starting with &lt;strong&gt;component testing&lt;/strong&gt; and then gradually increasing the scope.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your website is meant for internal or has limited access:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;on-premise load generators&lt;/strong&gt; located within the network that most of your users access the website from.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your website is external and public-facing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;cloud load generators&lt;/strong&gt; in the load zones where your users reside.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Test in &lt;strong&gt;pre-production environments&lt;/strong&gt; when you can, but also consider &lt;strong&gt;testing in production&lt;/strong&gt; in a limited capacity.&lt;/p&gt;
&lt;p&gt;Load testing websites can be complex due to the number of viable testing approaches available, the scope for performance testing, and the potential effects of releasing undertested code. By following the recommendations we&amp;rsquo;ve put forward here, you can tailor your testing more closely to your objectives.&lt;/p&gt;</description></item><item><title>Realistic Calendar Blocking for the Agile Knowledge Worker</title><link>https://nicolevanderhoeven.com/blog/20220201-realistic-calendar-blocking-for-the-agile-knowledge-worker/</link><pubDate>Tue, 01 Feb 2022 16:07:31 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220201-realistic-calendar-blocking-for-the-agile-knowledge-worker/</guid><description>&lt;p&gt;Calendar blocking is the only way to identify and avoid distraction.&lt;/p&gt;
&lt;p&gt;In a world that tempts us with &amp;ldquo;a little bit of everything, all of the time,&amp;rdquo; resisting distraction is the most valuable skill. Yet distraction isn&amp;rsquo;t always easy to recognize, especially when it comes in the guise of other things we need to do.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how to use calendar blocking to gain control over your schedule.&lt;/p&gt;
&lt;h2 id="set-realistic-schedules"&gt;Set realistic schedules.&lt;/h2&gt;
&lt;p&gt;Setting an overly ambitious schedule inevitably leads to disappointment.&lt;/p&gt;
&lt;p&gt;The hard part is figuring out what&amp;rsquo;s infeasible, and that has to come with time. In the meantime, &lt;em&gt;expect to change your schedule.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="use-digital-tools"&gt;Use digital tools.&lt;/h2&gt;
&lt;p&gt;Calendar Tetris isn&amp;rsquo;t fun&amp;ndash; but there are tools that can help.&lt;/p&gt;
&lt;p&gt;If you expect to change your schedule, choose tools that make that easier. I use &lt;a href="http://Reclaim.ai"&gt;Reclaim.ai&lt;/a&gt; to change my calendar in response to changing priorities.&lt;/p&gt;
&lt;h2 id="do-a-weekly-review"&gt;Do a weekly review.&lt;/h2&gt;
&lt;p&gt;Review how well you stuck to your schedule and adjust it accordingly.&lt;/p&gt;
&lt;p&gt;A good schedule, like a good budget, changes over time. Set aside a time every week to assess the previous week and set priorities for the next week.&lt;/p&gt;
&lt;h2 id="relate-your-schedule-to-your-core-principles"&gt;Relate your schedule to your core principles.&lt;/h2&gt;
&lt;p&gt;Your calendar should be the reflection of the values you hold.&lt;/p&gt;
&lt;p&gt;But what are those values? Keep a list of the principles you live by (or want to live by). Look at your calendar as a way to keep yourself on track.&lt;/p&gt;
&lt;h2 id="reframe-restriction-as-freedom"&gt;Reframe restriction as freedom.&lt;/h2&gt;
&lt;p&gt;When you don&amp;rsquo;t set your calendar, others set it for you.&lt;/p&gt;
&lt;p&gt;Restriction isn&amp;rsquo;t following a schedule &lt;em&gt;you&lt;/em&gt; set. Restriction is being subject to the passing whims of bugs and meetings that demand your attention.&lt;/p&gt;
&lt;h2 id="calendar-blocking-defends-your-time"&gt;Calendar blocking defends your time.&lt;/h2&gt;
&lt;p&gt;It frees you to do things you decide are important.&lt;/p&gt;</description></item><item><title>5 Things the Obsidian Templater Plugin Can Do That Templates Can't</title><link>https://nicolevanderhoeven.com/blog/20220131-5-things-the-obsidian-templater-can-do-that-templates-cant/</link><pubDate>Mon, 31 Jan 2022 22:46:52 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220131-5-things-the-obsidian-templater-can-do-that-templates-cant/</guid><description>&lt;p&gt;The Templater plugin is what the core Templates plugin should have been.&lt;/p&gt;
&lt;p&gt;The name is unfortunate, as it implies that the two plugins are similar. Turns out they&amp;rsquo;re only similar in that one is a limited version of the other.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what you can do with Templater&amp;ndash; and not with Templates.&lt;/p&gt;
&lt;h2 id="add-dynamic-variables-to-your-notes"&gt;Add dynamic variables to your notes&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;re probably thinking, &lt;em&gt;but Templates can already add dates&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Templater can do that, &lt;em&gt;and more&lt;/em&gt;. You can use it to fetch values in YAML frontmatter, random quotes from the interwebs, and even clipboard contents.&lt;/p&gt;
&lt;h2 id="make-your-notes-context-sensitive"&gt;Make your notes context-sensitive&lt;/h2&gt;
&lt;p&gt;Templater can create notes with different templates depending on context.&lt;/p&gt;
&lt;p&gt;For example, you could set up Obsidian to create a new note using the &lt;em&gt;Person&lt;/em&gt; template in the &lt;em&gt;People&lt;/em&gt; folder but a &lt;em&gt;Weekly Review&lt;/em&gt; note on Sunday nights.&lt;/p&gt;
&lt;h2 id="increment-your-notes-automatically"&gt;Increment your notes automatically&lt;/h2&gt;
&lt;p&gt;Templater can automatically increment new instances of a series.&lt;/p&gt;
&lt;p&gt;You can set it up to create a session log for your 100th D&amp;amp;D session, a review note for your 12th monthly review, or your 10th book review.&lt;/p&gt;
&lt;h2 id="interact-with-other-plugins"&gt;Interact with other plugins&lt;/h2&gt;
&lt;p&gt;Templater leverages all the best parts of Obsidian&amp;rsquo;s ecosystem.&lt;/p&gt;
&lt;p&gt;You can read from and write to YAML frontmatter using Dataview, or run commands that create notes based on text input with Buttons.&lt;/p&gt;
&lt;h2 id="write-and-run-your-own-scripts"&gt;Write and run your own scripts&lt;/h2&gt;
&lt;p&gt;Templater lets you write and call user scripts within an Obsidian note.&lt;/p&gt;
&lt;p&gt;This is the ultimate in extensibility. The ability to run custom JavaScript means you&amp;rsquo;re limited only by your imagination (and skill).&lt;/p&gt;
&lt;h2 id="templater-lets-you-turn-obsidian-into-exactly-what-you-want"&gt;Templater lets you turn Obsidian into exactly what you want.&lt;/h2&gt;
&lt;p&gt;And isn&amp;rsquo;t that what a PKM should do?&lt;/p&gt;</description></item><item><title>10 Mandatory Obsidian Plugins for Every Software Engineer</title><link>https://nicolevanderhoeven.com/blog/20220129-10-mandatory-obsidian-plugins-for-every-software-engineer/</link><pubDate>Sat, 29 Jan 2022 23:58:12 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220129-10-mandatory-obsidian-plugins-for-every-software-engineer/</guid><description>&lt;p&gt;If you haven&amp;rsquo;t installed any Obsidian plugins, you&amp;rsquo;re missing out.&lt;/p&gt;
&lt;p&gt;Many other mainstream PKM apps seem to have been built with the intention of hand-holding users through every step using a pretty interface. On the other hand, Obsidian is perfectly suited to power users who want to change the tool to meet their needs exactly. But you can only do that with plugins.&lt;/p&gt;
&lt;p&gt;Here are 10 Obsidian plugins you should install if you work in tech:&lt;/p&gt;
&lt;h2 id="enable-these-core-plugins"&gt;Enable these Core Plugins.&lt;/h2&gt;
&lt;p&gt;Core plugins are shipped with Obsidian but are disabled by default.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Daily notes&lt;/em&gt;&lt;/strong&gt;: Creates a new Markdown page according to a specified template that serves as a scratchpad and springboard for every new day.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Templates&lt;/em&gt;&lt;/strong&gt;: Lets you define predetermined formats that can be inserted into notes. Some template examples: Daily/Weekly/Monthly/Yearly notes, meeting notes with agendas, person notes to create your own CRM.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Backlinks&lt;/em&gt;:&lt;/strong&gt; When you have a note open, this shows you all the other notes that have referred to it, explicitly (via [[links]]) or implicitly (unlinked).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Quick switcher&lt;/em&gt;:&lt;/strong&gt; Lets you find any note by hitting CMD+O and showing search results as you type. With this enabled, I never bother with folders.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Workspaces&lt;/em&gt;:&lt;/strong&gt; Lets you save the exact arrangement of notes and sidebar options you have selected, and then load it quickly later. I have workspaces for writing, processing content, playing D&amp;amp;D, and making videos.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="install-these-community-plugins"&gt;Install these Community Plugins.&lt;/h2&gt;
&lt;p&gt;Community plugins are created by users and open-sourced on GitHub.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Calendar&lt;/em&gt;&lt;/strong&gt;: Adds a real-world calendar to your side panel that shows daily and weekly notes. Use Fantasy Calendar for even more customization.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Editor Syntax Highlight&lt;/em&gt;:&lt;/strong&gt; Formats text in code blocks according to the programming language set. Drastically improves readability of any code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Readwise Official&lt;/em&gt;:&lt;/strong&gt; Regularly imports your highlights from ebooks, web articles, audiobooks, tweets, and PDFs into your Obsidian vault.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Templater&lt;/em&gt;:&lt;/strong&gt; Extends templating functionality by letting you run JAVASCRIPT. In your vault! This blows the functionality of Obsidian wide open.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Dataview&lt;/em&gt;:&lt;/strong&gt; Turns your Obsidian vault into a database using custom parameters in YAML frontmatter. There&amp;rsquo;s even a way to write queries in js!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Why All Roads Lead to Testing</title><link>https://nicolevanderhoeven.com/blog/20220125-all-roads-lead-to-testing/</link><pubDate>Tue, 25 Jan 2022 23:59:03 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220125-all-roads-lead-to-testing/</guid><description>&lt;h1 id="why-all-roads-lead-to-testing"&gt;Why all roads lead to testing&lt;/h1&gt;
&lt;p&gt;Testing is a skill every software engineer should develop.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easy to dismiss testing as a responsibility only some carry, but it&amp;rsquo;s best thought of as a hat that &lt;em&gt;anyone&lt;/em&gt; can put on. How well you learn to wear this hat affects your career whether or not you&amp;rsquo;re a tester. Here&amp;rsquo;s why.&lt;/p&gt;
&lt;h2 id="ideas-are-cheap"&gt;Ideas are cheap.&lt;/h2&gt;
&lt;p&gt;In tech, having an idea isn&amp;rsquo;t what sells&amp;ndash; the success is in the execution.&lt;/p&gt;
&lt;p&gt;iPhones didn&amp;rsquo;t become popular because it was the first mobile with a touchscreen. The Nintendo Switch isn&amp;rsquo;t ubiquitous because of its revolutionary hardware. Behind every successful product is a lot of testing.&lt;/p&gt;
&lt;h2 id="testing-isnt-just-for-testers"&gt;Testing isn&amp;rsquo;t just for testers.&lt;/h2&gt;
&lt;p&gt;Every good software engineer does testing.&lt;/p&gt;
&lt;p&gt;Debugging is a form of testing. Questioning requirements is a form of testing. Testers don&amp;rsquo;t (and shouldn&amp;rsquo;t) have a monopoly on testing. The reality is that everyone is doing it, often without calling it &amp;ldquo;testing&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="testing-is-about-people-more-than-it-is-about-machines"&gt;Testing is about people more than it is about machines.&lt;/h2&gt;
&lt;p&gt;The fundamental goal of any app is to address a human need.&lt;/p&gt;
&lt;p&gt;Testing is finding new ways to represent and understand this need at all stages of development. Testing is a people problem, not a technical problem.&lt;/p&gt;
&lt;h2 id="humans-are-irrational"&gt;Humans are irrational.&lt;/h2&gt;
&lt;p&gt;The biggest challenge in building apps is human irrationality.&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t always &lt;em&gt;want&lt;/em&gt; what we need, we don&amp;rsquo;t always &lt;em&gt;know&lt;/em&gt; what we need, and even when we&amp;rsquo;re building &lt;em&gt;for&lt;/em&gt; others, we don&amp;rsquo;t always &lt;em&gt;work&lt;/em&gt; towards what we need. Testing is an attempt to make sense of this irrationality.&lt;/p&gt;
&lt;h2 id="testing-is-an-attitude"&gt;Testing is an attitude.&lt;/h2&gt;
&lt;p&gt;Getting good at testing extends to aspects beyond work.&lt;/p&gt;
&lt;p&gt;Testing is a discipline of questioning. It&amp;rsquo;s having curiosity as a default state and trying to meet people where they are. These are things every project, and every community, needs in spades.&lt;/p&gt;
&lt;h2 id="testing-is-rarely-the-destination"&gt;Testing is rarely the destination.&lt;/h2&gt;
&lt;p&gt;But some time, some how, all roads lead to testing.&lt;/p&gt;</description></item><item><title>How to Actually Remember What You Learn, for Engineers with Long Reading Lists</title><link>https://nicolevanderhoeven.com/blog/20220124-how-to-actually-remember-what-you-learn/</link><pubDate>Mon, 24 Jan 2022 21:47:59 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220124-how-to-actually-remember-what-you-learn/</guid><description>&lt;p&gt;Who says you need the terminology of the Zettelkasten process? Here&amp;rsquo;s how I&amp;rsquo;ve learned how to learn and how to apply new ideas to my daily work.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re reading this, you probably make your living on what you&amp;rsquo;ve learned that other people haven&amp;rsquo;t. Unfortunately, if you don&amp;rsquo;t have a written system to keep track of new ideas, you&amp;rsquo;ll probably unlearn everything in a few years.&lt;/p&gt;
&lt;h2 id="most-people-think-reading--learning"&gt;Most people think reading = learning.&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s why reading isn&amp;rsquo;t enough.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Our brains are best at linking concepts, not at remembering them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Writing doesn&amp;rsquo;t help us learn; it IS how we learn.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can&amp;rsquo;t learn to swim by reading about it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If reading isn&amp;rsquo;t enough, then what should you do instead?&lt;/p&gt;
&lt;h3 id="step-1-consume"&gt;&lt;strong&gt;Step 1: Consume&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The first step is to consume content, but &lt;em&gt;selectively&lt;/em&gt; so.&lt;/p&gt;
&lt;p&gt;Content could be a book, a video, a podcast, an article, or even a conversation. &lt;em&gt;Take notes&lt;/em&gt; on the author&amp;rsquo;s main points.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; Shape Up is a methodology emphasising iterative cycles, &amp;ldquo;betting&amp;rdquo; on work to be done, and building fully integrated slices of work.&lt;/p&gt;
&lt;h3 id="step-2-redefine"&gt;&lt;strong&gt;Step 2: Redefine&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Next, go beyond facts and add your thoughts.&lt;/p&gt;
&lt;p&gt;Think about the broader picture of what the author might be saying, and supplement it with &lt;em&gt;your&lt;/em&gt; opinions. &lt;em&gt;Example:&lt;/em&gt; Shape Up is an Agile methodology concerned with the limits we should apply to different stages of work.&lt;/p&gt;
&lt;h3 id="step-3-contextualize"&gt;&lt;strong&gt;Step 3: Contextualize&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Think about what the content means to you, specifically&amp;ndash; how does it stack with other things you&amp;rsquo;ve learned? Where does it fit into your interests? Accept that your context, and your notes, will change.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; Shape Up&amp;rsquo;s idea of fully integrated work supports the notion of context-switching (see: Continuous integration, Multithreading) but could be in contention with productivity concepts like deep work (David Allen), batching, and Productivity Momentum.&lt;/p&gt;
&lt;h2 id="its-your-brain"&gt;It&amp;rsquo;s your brain!&lt;/h2&gt;
&lt;p&gt;In the end, nobody can tell you the &amp;ldquo;right&amp;rdquo; way to learn.&lt;/p&gt;</description></item><item><title>My Workflow for Making Tech and Travel Videos Online</title><link>https://nicolevanderhoeven.com/blog/20220123-my-workflow-for-making-tech-and-travel-videos-online/</link><pubDate>Sun, 23 Jan 2022 16:16:33 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220123-my-workflow-for-making-tech-and-travel-videos-online/</guid><description>&lt;p&gt;In the last 5 years, I&amp;rsquo;ve made ~220 videos across 4 YouTube channels. Here&amp;rsquo;s what I&amp;rsquo;ve learned so far about making videos.&lt;/p&gt;
&lt;h2 id="why-make-videos"&gt;&lt;strong&gt;Why make videos?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A 10-minute video takes me anywhere from 7 to 15 hours to produce. I wouldn&amp;rsquo;t be doing it if I didn&amp;rsquo;t enjoy it!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It can be the most genuine form of expression.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Videos were a big part of how &lt;em&gt;I&lt;/em&gt; got into tech, and I want to pass that on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I&amp;rsquo;d rather make a video once than explain the same thing to 100 people.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don&amp;rsquo;t think my way is the &lt;em&gt;best&lt;/em&gt; way, but maybe sharing my workflow will make it easier for you to get started, too!&lt;/p&gt;
&lt;h3 id="pre-production-ideation"&gt;&lt;strong&gt;Pre-production: Ideation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Ideas are cheap&lt;/strong&gt;. The best idea is worthless if I can&amp;rsquo;t convey it well.&lt;/p&gt;
&lt;p&gt;I start with the idea, then the title and thumbnail. I search YouTube to see what titles have worked for others. Then, I use @Canva for thumbnail creation and &lt;a href="http://thumbsup.tv"&gt;thumbsup.tv&lt;/a&gt; to test combinations.&lt;/p&gt;
&lt;p&gt;Only then do I start to outline the video or research it. I use @obsdmd as my project management tool and content calendar.&lt;/p&gt;
&lt;h3 id="production-filming-and-editing"&gt;&lt;strong&gt;Production: Filming and editing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;I like to film with talking points rather than a full script. It saves me time, and it comes across as more natural. My main gear: Sony A6600 + Sigma 16mm f/1.4, Røde PodMic, Elgato keylight.&lt;/p&gt;
&lt;p&gt;I edit everything myself in Final Cut Pro, using stock footage from @StoryblocksCo and sound effects/music from @epidemicsound.&lt;/p&gt;
&lt;h3 id="post-production-publishing"&gt;&lt;strong&gt;Post-production: Publishing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;I export and upload the video to YouTube, adding the title, thumbnail, description, cards, end screen&amp;hellip; I have a checklist so I don&amp;rsquo;t forget these.&lt;/p&gt;
&lt;p&gt;I send most videos to humans at @Rev for captioning. I then use the captions to create YouTube chapters. From there, it&amp;rsquo;s just about publishing to appropriate platforms.&lt;/p&gt;
&lt;h2 id="optimize-for-consistency-and-fun"&gt;Optimize for consistency and fun&lt;/h2&gt;
&lt;p&gt;When in doubt, I chuck technique out the window and do what will keep video creation fun for me!&lt;/p&gt;</description></item><item><title>How to Do What Kind of Testing When: Testing Types by Scope</title><link>https://nicolevanderhoeven.com/blog/20220122-how-to-do-what-kind-of-testing-when/</link><pubDate>Sat, 22 Jan 2022 16:42:49 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220122-how-to-do-what-kind-of-testing-when/</guid><description>&lt;p&gt;Correctly identifying &lt;em&gt;when&lt;/em&gt; you test could be just as important as &lt;em&gt;what&lt;/em&gt;. Below are types of testing according to scope.&lt;/p&gt;
&lt;h3 id="static-testing"&gt;Static testing&lt;/h3&gt;
&lt;p&gt;Static testing occurs when requirements are written. Question requirements and spell out what each one means.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Concerns:&lt;/em&gt; Setting up a system to track and link requirements to test cases, negotiating business needs with technical feasibility, clarifying scope.&lt;/p&gt;
&lt;h3 id="unit-testing"&gt;Unit testing&lt;/h3&gt;
&lt;p&gt;Unit testing occurs when coding begins. Practice Test-Driven Development by translating requirements into tests before writing the code.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Concerns:&lt;/em&gt; Treating test code like application code, getting developer buy-in, communicating technical implementation, performance, pair programming.&lt;/p&gt;
&lt;h3 id="system-testing"&gt;System testing&lt;/h3&gt;
&lt;p&gt;System testing occurs when a build is released. Employ black-box testing: step back from the code and assess the feature as a whole for consistency.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Concerns:&lt;/em&gt; Adherence to common design standards, equivalence partitioning, boundary value analysis, fighting scope creep, usability, creating test data.&lt;/p&gt;
&lt;h3 id="system-integration-testing-sit"&gt;System integration testing (SIT)&lt;/h3&gt;
&lt;p&gt;SIT occurs when integrating multiple components. Use white-box or grey-box testing to target the interactions and traffic between components.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Concerns:&lt;/em&gt; API testing, stubbing and mocking, identifying request paths throughout the system, performance benchmarking, observability and analysis.&lt;/p&gt;
&lt;h3 id="business-acceptance-testing-bat"&gt;Business acceptance testing (BAT)&lt;/h3&gt;
&lt;p&gt;BAT occurs when the code is assessed against overall business goals. Seek out SMEs who can help you verify expected outcomes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Concerns:&lt;/em&gt; Adherence to business requirements and regulations, process testing, simplifying request flows, identifying potential customer pain points.&lt;/p&gt;
&lt;h3 id="user-acceptance-testing-uat"&gt;User acceptance testing (UAT)&lt;/h3&gt;
&lt;p&gt;User acceptance testing occurs before code is released to production. Map test cases to requirements and test real-world scenarios.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Concerns:&lt;/em&gt; Testing for superuser workflows, data cleansing, verifying real user behavior from historical logs, exploratory testing, end-to-end performance.&lt;/p&gt;
&lt;h3 id="regression-testing"&gt;Regression Testing&lt;/h3&gt;
&lt;p&gt;Regression testing occurs between builds. Supplement a robust automation test suite with nuanced manual test cases that address known problem areas.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Concerns:&lt;/em&gt; Setting up a CI/CD pipeline, deciding what to automate, consolidating test and development tools, tracking failures over time.&lt;/p&gt;
&lt;h3 id="question-everything"&gt;Question everything&lt;/h3&gt;
&lt;p&gt;Remember: &lt;strong&gt;testing is an attitude.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>How to Use Executors in k6: Adapting Load Test Workload Models to Production</title><link>https://nicolevanderhoeven.com/blog/20220118-how-to-use-executors-in-k6-adapting-load-test-workload-models-to-production/</link><pubDate>Tue, 18 Jan 2022 22:45:52 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220118-how-to-use-executors-in-k6-adapting-load-test-workload-models-to-production/</guid><description>&lt;p&gt;Executors in k6 are like traffic cops.&lt;/p&gt;
&lt;p&gt;They define the shape and volume of traffic that your k6 load testing script follows during test execution. Executors can also drastically affect your results if you don&amp;rsquo;t get them right.&lt;/p&gt;
&lt;p&gt;Here are all the executor options in k6 and when you should use each one:&lt;/p&gt;
&lt;h2 id="k6-executors"&gt;k6 executors&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;shared-iterations&lt;/em&gt;&lt;/strong&gt;: Fix the number of total iterations for the entire test. Use when you want to create precisely X rows of test data during a prep run.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;per-vu-iterations&lt;/em&gt;&lt;/strong&gt;: Fix the number of iterations each virtual user (VU) does. Use when you want each VU to use exactly X rows of test data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;constant-vus&lt;/em&gt;&lt;/strong&gt;: Fix the number of VUs and duration, but allow the number of iterations to vary. Use for straightforward tests or shakeout tests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;ramping-vus&lt;/em&gt;&lt;/strong&gt;: Set stages, each with a number of VUs and a duration, and let the number of iterations vary. Use for easiest comparison with other load testing tools and for custom load profiles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;constant-arrival-rate&lt;/em&gt;&lt;/strong&gt;: Fix the number of requests per second (rps) and duration, and allow the number of VUs and iterations to vary. Use when you have a simple load profile with a request throughput requirement (rps).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;ramping-arrival rate&lt;/em&gt;&lt;/strong&gt;: Like &lt;strong&gt;&lt;em&gt;constant-arrival-rate&lt;/em&gt;&lt;/strong&gt;, but with stages. For each stage, fix the number of rps and duration. Use for spike tests, multiphase load profiles with distinct requirements, and Game Day simulations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;externally-controlled&lt;/em&gt;&lt;/strong&gt;: Fix max VUs and time, and allow everything else to be changed at runtime. Use for disaster recovery, breakpoint, or stress tests where you want ultimate flexibility in adapting your test to real-time results.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="still-unsure"&gt;Still unsure?&lt;/h2&gt;
&lt;p&gt;Use &lt;strong&gt;&lt;em&gt;ramping-vus&lt;/em&gt;&lt;/strong&gt;. It&amp;rsquo;s probably what you&amp;rsquo;re used to from other tools.&lt;/p&gt;
&lt;p&gt;Ready to move up? Use &lt;strong&gt;&lt;em&gt;ramping-arrival-rate&lt;/em&gt;&lt;/strong&gt;. Rps is a better (but still not perfect) measure of throughput for comparison than just the number of VUs.&lt;/p&gt;
&lt;h2 id="when-in-doubt-look-at-production"&gt;When in doubt, look at production.&lt;/h2&gt;
&lt;p&gt;Making sure your test looks and acts like real traffic yields the best results!&lt;/p&gt;
&lt;p&gt;For more details, check out &lt;a href="https://k6.io/docs/using-k6/scenarios/#executors"&gt;the k6 documentation&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Testing tool cheat sheet for load testers who don't just care about numbers</title><link>https://nicolevanderhoeven.com/blog/20220117-testing-tool-cheat-sheet-for-load-testers-who-dont-just-care-about-numbers/</link><pubDate>Mon, 17 Jan 2022 16:30:07 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220117-testing-tool-cheat-sheet-for-load-testers-who-dont-just-care-about-numbers/</guid><description>&lt;p&gt;It&amp;rsquo;s not enough to decide &lt;em&gt;how much&lt;/em&gt; load you want to generate. You also have to know &lt;em&gt;how&lt;/em&gt; you&amp;rsquo;re generating it.&lt;/p&gt;
&lt;p&gt;Your approach affects what part of your application is tested, the results returned, and the budget you&amp;rsquo;ll need to run your test.&lt;/p&gt;
&lt;p&gt;Here are three main categories of load testing tools:&lt;/p&gt;
&lt;h2 id="protocol-based-load-testing-tools"&gt;Protocol-based load testing tools:&lt;/h2&gt;
&lt;p&gt;These tools simulate the underlying messages sent back and forth between an application and a client.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Choose a protocol-based tool if&lt;/em&gt;: you want to generate massive load, you want to load test before components are integrated, you want to test APIs, or you are budget-conscious.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Best in class tools&lt;/em&gt;: Gatling, JMeter, k6, Locust, Neoload&lt;/p&gt;
&lt;h2 id="browser-based-load-testing-tools"&gt;Browser-based load testing tools:&lt;/h2&gt;
&lt;p&gt;These tools automate real user interactions with an interface.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Choose a browser-based tool if:&lt;/em&gt; you want to measure end-to-end performance, you want to reuse your browser automation scripts, you want front-end performance metrics, or you have a higher budget.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Best in class tools:&lt;/em&gt; Cypress, Flood Element, k6, Playwright, Puppeteer&lt;/p&gt;
&lt;h2 id="hybrid-load-testing-tools"&gt;Hybrid load testing tools:&lt;/h2&gt;
&lt;p&gt;This approach is a mixture of the previous two. You can do this in a few ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a protocol tool to generate the majority of the load, and a browser tool for a handful of virtual users &lt;em&gt;(best functionality, highest effort/cost)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Use a browser-based tool for all load &lt;em&gt;(expensive, simplest)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Find a tool that can do both &lt;em&gt;(ideal solution, few options)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="whatever-you-choose-compare-like-with-like"&gt;Whatever you choose, compare like with like.&lt;/h2&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;</description></item><item><title>Building a DM's Brain Attic</title><link>https://nicolevanderhoeven.com/blog/20220115-building-a-dms-brain-attic/</link><pubDate>Sat, 15 Jan 2022 16:45:22 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220115-building-a-dms-brain-attic/</guid><description>&lt;p&gt;Anyone other DMs have this problem? My TRPGs folder, containing paid third-party assets for D&amp;amp;D and other RPGs, is made up of 37,884 items (287 GB).&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how I make sure I actually get value out of this embarrassment of riches: 👇&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/trpgs.png" alt="Screenshot of my TRPGs folder with all my third-party items" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Mike from &lt;a href="https://twitter.com/SlyFlourish"&gt;@SlyFlourish&lt;/a&gt; wrote an article applying Sherlock Holmes&amp;rsquo;s brain attic for lazy dungeon masters, and he called it the &amp;ldquo;[DM Brain Attic](&lt;a href="https://slyflourish.com/brain_attic.html%22"&gt;https://slyflourish.com/brain_attic.html&amp;quot;&lt;/a&gt;. The idea is to collect interesting techniques or ideas and file them away for later.&lt;/p&gt;
&lt;p&gt;Sounds like a good idea, but for most of us, this attic is theoretical. We read books, we watch videos, and we read blog posts on monsters and settings and plots, and we trust that they&amp;rsquo;ll all be saved somewhere in our heads.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not how it works. Our minds are good at making connections, not remembering CRs from monster statblocks. So how do we make this DM Brain Attic less theoretical and more practical?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read and take notes. If you haven&amp;rsquo;t read everything you&amp;rsquo;ve bought, you&amp;rsquo;re buying too much. Read what you&amp;rsquo;ve got and write down the parts that are actually relevant to you and your game. Here&amp;rsquo;s what mine looks like for the first Arcadia (@helloMCDM) issue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/arcadia.png" alt="Screenshot of my notes on Arcadia issue 1" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Combine like with like. This is the hard part, because it involves creating a central place for monsters, items, spells, etc. &lt;a href="https://github.com/nicolevanderhoeven/xios-guide-to-monsters"&gt;This&lt;/a&gt; is my attempt to do this for monsters. It creates one page per monster that includes its source. Here&amp;rsquo;s a monster example.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/zombie.png" alt="Screenshot of a monster page" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example for items that includes SRD items and Vault of Magic Items (by &lt;a href="https://twitter.com/KoboldPress"&gt;@KoboldPress&lt;/a&gt;). Each one links to a page for the item with full details.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/vault-of-magic.png" alt="Screenshot of my page of items" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Resurface via search: You may have noticed the metadata in the previous page. The metadata gives me criteria to search monsters by. Here&amp;rsquo;s how I can query my monster database using the &lt;a href="https://github.com/blacksmithgu/obsidian-dataview"&gt;Dataview plugin&lt;/a&gt; for &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/dataview-query.png" alt="Screenshot of a monster query using Dataview" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s the result of that query, returning monsters that fit my criteria and, importantly, which of my PDFs they come from.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/dataview-result.png" alt="Screenshot of the results from the Dataview query shown earlier" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Resurface via random table generators. Another way that I resurface content is by using random table generators using the &lt;a href="https://github.com/valentine195/obsidian-dice-roller"&gt;Dice Roller plugin&lt;/a&gt; for Obsidian by Jeremy Valentine. Here&amp;rsquo;s my NPC page, compiled from multiple sources.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/npc-rolls.png" alt="My NPC creation page with randomized traits" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Each die icon can be clicked to generate a new &amp;ldquo;roll&amp;rdquo;. Here&amp;rsquo;s a gif for how that works, using stuff from Sly Flourish&amp;rsquo;s Uncovered Secrets, accessible to &lt;a href="https://www.patreon.com/slyflourish"&gt;his Patreons&lt;/a&gt;. Each click &amp;ldquo;rolls&amp;rdquo; on a table I&amp;rsquo;ve defined in a separate note.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/uncoveredsecrets_02.gif" alt="Gif of how my random tables created from Sly Flourish’s Uncovered Secrets" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Is building a DM Brain Attic a lot of work? Yes.&lt;/p&gt;
&lt;p&gt;But how likely is it that I&amp;rsquo;d use my 287 GB of third-party stuff while preparing for my game if I didn&amp;rsquo;t have this system? Not very.&lt;/p&gt;
&lt;p&gt;At least this way, I&amp;rsquo;m using what I&amp;rsquo;ve actually bought and putting good content into a very real DM&amp;rsquo;s Brain Attic that I can actually scrounge around for good ideas in, before or during a game.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to know more, check out this video I made about how I use Obsidian to run and play D&amp;amp;D online:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/3pt6_srUZ7U?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Financial Independence Isn't Just for Old People</title><link>https://nicolevanderhoeven.com/blog/20220113-financial-independence-isnt-just-for-old-people/</link><pubDate>Thu, 13 Jan 2022 16:43:46 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220113-financial-independence-isnt-just-for-old-people/</guid><description>&lt;p&gt;5 years ago, at age 31, I became financially independent.&lt;/p&gt;
&lt;p&gt;Financial independence (FI) means that if I wanted to retire tomorrow, I could. I&amp;rsquo;d have to make significant changes to my life to do it, but I&amp;rsquo;d have a plan, and enough resources at hand to get by.&lt;/p&gt;
&lt;p&gt;Unfortunately, just talking about FI is uncommon at best and taboo at worst, even in an industry with higher-than-average salaries like tech.&lt;/p&gt;
&lt;p&gt;You might be thinking that I received some sort of inheritance or unexpected windfall. The truth is that 10 years ago, my husband and I were in credit card debt and had no major assets. Now he&amp;rsquo;s retired, and I have a job I love.&lt;/p&gt;
&lt;p&gt;The funny thing is, there&amp;rsquo;s really nothing magical about how we got here.&lt;/p&gt;
&lt;h2 id="heres-how-you-can-do-it-too"&gt;Here&amp;rsquo;s how you can do it too.&lt;/h2&gt;
&lt;h3 id="step-1-track-your-money"&gt;&lt;strong&gt;Step 1: Track your money.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;You can&amp;rsquo;t fix something you aren&amp;rsquo;t monitoring.&lt;/p&gt;
&lt;p&gt;To this day, my husband and I use (&lt;a href="http://youneedabudget.com"&gt;YNAB&lt;/a&gt;) to track every cent of our money, whether we&amp;rsquo;re saving it or spending it. When your financial situation is right there in black and white, it&amp;rsquo;s hard to ignore.&lt;/p&gt;
&lt;h3 id="step-2-reduce-your-expenses"&gt;&lt;strong&gt;Step 2: Reduce your expenses.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Too many people focus on earning more, when the reality is that it&amp;rsquo;s far easier to adjust what you&amp;rsquo;re &lt;em&gt;spending&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You can afford (almost) anything, but you can&amp;rsquo;t afford everything&amp;ndash; so figure out what matters. Reducing your expenses is even better than getting a raise. Do regular audits of fixed expenses. If you have a partner, consider living on less than one income and banking the other entirely.&lt;/p&gt;
&lt;h3 id="step-3-automate-investments"&gt;&lt;strong&gt;Step 3: Automate investments.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Set up a recurring monthly payment for a painful amount of money. Invest in low-cost ETFs like Vanguard&amp;rsquo;s &lt;em&gt;and then forget about them&lt;/em&gt;. Resign yourself to living at your current lifestyle level (or lower), and don&amp;rsquo;t live it up when you get a raise.&lt;/p&gt;
&lt;p&gt;And for those who want a goal post, as I did: &lt;strong&gt;Actually calculate the minimum amount you&amp;rsquo;d need to retire&lt;/strong&gt;. It may be less than you think.&lt;/p&gt;
&lt;p&gt;Money isn&amp;rsquo;t everything, but it can be. To someone who doesn&amp;rsquo;t have it, money can buy freedom.&lt;/p&gt;</description></item><item><title>What Tabletop Games Teach Us About Application Perfomance</title><link>https://nicolevanderhoeven.com/blog/20220112-what-tabletop-games-teach-us-about-application-perfomance/</link><pubDate>Wed, 12 Jan 2022 16:30:29 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220112-what-tabletop-games-teach-us-about-application-perfomance/</guid><description>&lt;p&gt;Tabletop games and application performance have more in common than you might think.&lt;/p&gt;
&lt;p&gt;See, it&amp;rsquo;s all about optimization. Both involve juggling several variables: some that you can control, and some that you can&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what I&amp;rsquo;ve learned about application performance from playing tabletop games.&lt;/p&gt;
&lt;h2 id="1-get-more-workers"&gt;#1: Get more workers&lt;/h2&gt;
&lt;p&gt;In worker placement games, you should always spend your first worker to train more workers.&lt;/p&gt;
&lt;p&gt;Similarly, one of the most basic ways to increase concurrency is multithreading: using more than one thread at a time. (See: Golang) This applies to testing tools as much as to systems. The 1 Thread: 1 Virtual User paradigm is flawed.&lt;/p&gt;
&lt;h2 id="2-spend-time-on-getting-your-engine-going"&gt;#2: Spend time on getting your engine going&lt;/h2&gt;
&lt;p&gt;Get your big combos in place before you tap to attack.&lt;/p&gt;
&lt;p&gt;Things like setting up automation frameworks or CI/CD pipelines DO set you back in the beginning, but they are worth their mana cost in the long run.&lt;/p&gt;
&lt;h2 id="3draft-with-others-in-mind"&gt;#3: Draft with others in mind&lt;/h2&gt;
&lt;p&gt;The trick to drafting is knowing what you&amp;rsquo;re leaving for others as much as what you&amp;rsquo;re taking for yourself.&lt;/p&gt;
&lt;p&gt;The short game is getting your team to do the testing. The long game is getting everyone in the company involved in testing.&lt;/p&gt;
&lt;h2 id="4-hope-for-a-nat-20-plan-for-a-nat-1"&gt;#4: Hope for a nat 20, plan for a nat 1&lt;/h2&gt;
&lt;p&gt;Sometimes proficiency, expertise, and a luckstone just aren&amp;rsquo;t enough.&lt;/p&gt;
&lt;p&gt;Instead of wondering &lt;em&gt;whether&lt;/em&gt; an app will fail, take for granted that it will, and start planning for it. Keep that Action Surge in your back pocket.&lt;/p&gt;
&lt;h2 id="5-sometimes-you-just-have-to-trade-for-sheep"&gt;#5: Sometimes, you just have to trade for sheep&lt;/h2&gt;
&lt;p&gt;The 2:1 port is valuable, but not always the best option.&lt;/p&gt;
&lt;p&gt;Learn what you can, but accept that you can&amp;rsquo;t learn it all. Reach out to others with compatible skillsets and ask the &amp;ldquo;stupid&amp;rdquo; questions that nobody else wants to ask.&lt;/p&gt;
&lt;h2 id="testing-is-a-team-sport-and-everyone-gets-a-chance-to-play"&gt;Testing is a team sport, and everyone gets a chance to play.&lt;/h2&gt;
&lt;p&gt;Happy gaming!&lt;/p&gt;</description></item><item><title>The Tester's Toolbox Is Flawed</title><link>https://nicolevanderhoeven.com/blog/20220111-the-testers-toolbox-is-flawed/</link><pubDate>Tue, 11 Jan 2022 16:30:49 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220111-the-testers-toolbox-is-flawed/</guid><description>&lt;p&gt;Every tester has a toolbox.&lt;/p&gt;
&lt;p&gt;In the toolbox rests a variety of tools for every conceivable purpose. Part of our job, as testers, is to choose the right one for the job. Sounds reasonable, right?&lt;/p&gt;
&lt;p&gt;That kind of thinking could be dangerous.&lt;/p&gt;
&lt;h2 id="the-right-tool-for-every-job-fails-for-multiple-jobs"&gt;&amp;ldquo;The right tool for every job&amp;rdquo; fails for multiple jobs.&lt;/h2&gt;
&lt;p&gt;You end up with multiple tools. One for accessibility testing. Another for API testing. Another for browser testing. Another for load testing. &lt;em&gt;This is the modern tester&amp;rsquo;s dilemma&lt;/em&gt;: we spend more time working with our tools than with the application under test.&lt;/p&gt;
&lt;p&gt;In the real world, including a new tool in your testing stack means a new framework, usually a new programming language, and sometimes even new colleagues with experience in those tools. And what about continuous testing? All those tools have to talk to each other at some point.&lt;/p&gt;
&lt;p&gt;More tools = more time, effort, and money.&lt;/p&gt;
&lt;h3 id="when-choosing-tools-prioritize-multitools"&gt;&lt;strong&gt;When choosing tools, prioritize multitools.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A multitool can be used for different purposes and prioritizes flexibility.&lt;/p&gt;
&lt;p&gt;Here are a few ways to spot a multitool:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;#1: It is a polymath.&lt;/strong&gt; A multitool is good at many things and is limited in use case only by the imagination of those that use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;#2: It uses what you know.&lt;/strong&gt; A multitool is built on languages and frameworks you and your team already know, reducing ramp-up time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;#3 It plays well with others.&lt;/strong&gt; A multitool doesn&amp;rsquo;t force you to use it to the exclusion of everything else. It integrates well with best-in-class tools.&lt;/p&gt;
&lt;p&gt;When you optimize for individual jobs, you&amp;rsquo;re missing the big picture. You&amp;rsquo;re ultimately left holding the ends of multiple tools and creating a kraken of a test suite with all these tools cobbled together.&lt;/p&gt;
&lt;p&gt;Instead, prioritize multitools.&lt;/p&gt;</description></item><item><title>Obsidian for the Uninitiated</title><link>https://nicolevanderhoeven.com/blog/20220110-obsidian-for-the-uninitiated/</link><pubDate>Mon, 10 Jan 2022 23:07:06 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220110-obsidian-for-the-uninitiated/</guid><description>&lt;h1 id="obsidian-for-the-uninitiated"&gt;Obsidian for the Uninitiated&lt;/h1&gt;
&lt;p&gt;You&amp;rsquo;ve installed Obsidian and created a new vault. Now what?&lt;/p&gt;
&lt;p&gt;Unfortunately, it can be more than a little daunting to get started with. It doesn&amp;rsquo;t help that there are terms like &lt;em&gt;Zettelkasten&lt;/em&gt; and &lt;em&gt;Maps of Content&lt;/em&gt; and &lt;em&gt;fleeting notes&lt;/em&gt; and &lt;em&gt;digital garden&lt;/em&gt; that are all just trying to structure what we all learned to do in primary school.&lt;/p&gt;
&lt;h2 id="start-by-logging-what-youre-doing"&gt;Start by logging what you&amp;rsquo;re doing.&lt;/h2&gt;
&lt;p&gt;Having a rigid structure does wonders for curtailing creativity and plain fun. Instead, I recommend starting small.&lt;/p&gt;
&lt;p&gt;Here are some small steps to take after installing Obsidian:&lt;/p&gt;
&lt;h3 id="step-1-enable-the-daily-notes-plugin"&gt;&lt;strong&gt;Step 1: Enable the Daily Notes plugin&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The most game-changing feature of Obsidian is plugins.&lt;/p&gt;
&lt;p&gt;Go into Settings &amp;gt; Core Plugins, and enable the &lt;em&gt;Daily notes&lt;/em&gt; plugin.&lt;/p&gt;
&lt;p&gt;Then, exit out of the dialog boxes and hit CMD P (Mac) or CTRL P (Windows) to bring up the command pane. Search for and select &lt;em&gt;Daily notes: Open today&amp;rsquo;s daily note&lt;/em&gt;. That creates a new note in your vault with today&amp;rsquo;s date.&lt;/p&gt;
&lt;p&gt;From now on, do this every day and log things you do during the day: things to do, appointments to keep, videos you watched.&lt;/p&gt;
&lt;h3 id="step-2-the-next-time-you-consume-content-take-notes"&gt;&lt;strong&gt;Step 2: The next time you consume content, take notes.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now, the next time you read or watch something interesting, type its title into Obsidian with brackets:&lt;code&gt;[[Cool video I watched]]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s now a link. CMD+click on that link, or click the glasses icon on the top right to switch to Preview mode, and then click on the link. You should now have a new note. Write about what you thought of the video.&lt;/p&gt;
&lt;h3 id="step-3-start-identifying-and-fleshing-out-ideas"&gt;&lt;strong&gt;Step 3: Start identifying and fleshing out ideas.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Keep going with these steps and every now and then, start to identify cool ideas you come across. Create new notes from them just like you did in Step 2. Don&amp;rsquo;t worry about getting it all; just do it when and until you feel like it.&lt;/p&gt;
&lt;p&gt;These three simple steps, over time, will be the foundations of your own Personal Knowlege Management system with Obsidian.&lt;/p&gt;</description></item><item><title>5 Ways to Use Obsidian as a Software Engineer</title><link>https://nicolevanderhoeven.com/blog/20220109-5-ways-to-use-obsidian-as-a-software-engineer/</link><pubDate>Sun, 09 Jan 2022 19:20:49 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220109-5-ways-to-use-obsidian-as-a-software-engineer/</guid><description>&lt;p&gt;Yesterday, I wrote about why I think taking notes is the surprising foundation of a tester&amp;rsquo;s career.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s not just testers. Every software engineer is a knowledge worker, and that means we get paid for what we know. Using a Personal Knowledge Management (PKM) system such as &lt;a href="http://Obsidian.md"&gt;Obsidian.md&lt;/a&gt; helps us keep up with the constant barrage of new ideas in tech.&lt;/p&gt;
&lt;p&gt;Here are 5 things I use Obsidian for.&lt;/p&gt;
&lt;h2 id="1-my-very-own-stack-overflow"&gt;#1: My very own Stack Overflow&lt;/h2&gt;
&lt;p&gt;If you code, you&amp;rsquo;ve used Stack Overflow.&lt;/p&gt;
&lt;p&gt;What if you had your &lt;strong&gt;own&lt;/strong&gt; Stack Overflow, one that contained cheat sheets of every language you&amp;rsquo;ve ever tried to hack something up in, including only commands relevant to your job? This is exactly &lt;a href="https://notes.nicolevanderhoeven.com/Python"&gt;what I&amp;rsquo;ve done for Python&lt;/a&gt; recently, and it&amp;rsquo;s helping me solidify my knowledge.&lt;/p&gt;
&lt;h2 id="2-tools-database"&gt;#2: Tools database&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a tool for everything.&lt;/p&gt;
&lt;p&gt;To keep them straight, I have a page for every load testing tool I try, with tutorials for how to get started, an assessment of its features, and observations I had while attempting to learn it. &lt;a href="https://notes.nicolevanderhoeven.com/k6+(tool)"&gt;Here&amp;rsquo;s what I have for the load testing tool k6&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bonus: I also use &lt;a href="https://github.com/blacksmithgu/obsidian-dataview"&gt;the Dataview plugin&lt;/a&gt; to query tools that fit my criteria.&lt;/p&gt;
&lt;h2 id="3-an-agile-workspace"&gt;#3: An Agile workspace&lt;/h2&gt;
&lt;p&gt;If Agile is good enough for my professional work, it&amp;rsquo;s good enough for my personal work.&lt;/p&gt;
&lt;p&gt;I use the &lt;a href="https://github.com/mgmeyers/obsidian-kanban"&gt;Kanban plugin&lt;/a&gt; to keep track of projects, create weekly and monthly review/retro templates, and work iteratively using &lt;a href="https://github.com/bjsi/incremental-writing"&gt;the Incremental Writing plugin&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="4-a-public-learning-log"&gt;#4: A public learning log&lt;/h2&gt;
&lt;p&gt;Learning in public is the fastest way to improve.&lt;/p&gt;
&lt;p&gt;When I&amp;rsquo;m trying to figure something out, I document my attempts. With Obsidian Publish, I can quickly push this log out to my domain and share them with colleagues to get a quick review so that they can easily follow my train of thought and tell me where I went wrong.&lt;/p&gt;
&lt;h2 id="5-a-digital-garden-of-software-ideas"&gt;#5: A digital garden of software ideas&lt;/h2&gt;
&lt;p&gt;Build libraries, not feeds.&lt;/p&gt;
&lt;p&gt;I use Obsidian to save things I read/watch/listen to but also to save space for ideas I&amp;rsquo;ve learned to germinate. I publish notes before they&amp;rsquo;re fully fleshed out, with the goal of growing everything over time.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://notes.nicolevanderhoeven.com"&gt;Today, I have 4,567 notes&lt;/a&gt;, all about things that matter to me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Every engineer should have a PKM system.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Take Notes, Not Tools</title><link>https://nicolevanderhoeven.com/blog/20220108-take-notes-not-tools/</link><pubDate>Sat, 08 Jan 2022 17:00:44 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20220108-take-notes-not-tools/</guid><description>&lt;p&gt;When I first got into testing, I thought that testing was about learning different tools and frameworks.&lt;/p&gt;
&lt;p&gt;I learned how to use Neoload, then LoadRunner, then JMeter, and then SeleniumSilkPerformerVisualStudioGatlingPuppeteerPlaywrightk6 in short succession. I took courses on JavaScript, on C, on Java. I ticked off tools like a grocery list.&lt;/p&gt;
&lt;p&gt;It didn&amp;rsquo;t take me long to realize that I still wasn&amp;rsquo;t a good tester.&lt;/p&gt;
&lt;h2 id="taking-notes-not-tools-is-what-makes-a-good-tester"&gt;Taking notes, not tools, is what makes a good tester.&lt;/h2&gt;
&lt;p&gt;When you think about it, a lot of things a tester does revolves around taking good notes.&lt;/p&gt;
&lt;p&gt;Requirements are just notes on what the business wants to achieve. Test scenarios are notes on situations that the team wants to simulate. Test cases are notes on how a user would use an application. Defects are notes on issues.&lt;/p&gt;
&lt;p&gt;The quality of a tester&amp;rsquo;s notes dictates the quality of their work.&lt;/p&gt;
&lt;h3 id="listen-to-people-not-tool-tutorials"&gt;&lt;strong&gt;Listen to people, not tool tutorials&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Take notes on what users do.&lt;/strong&gt; Talk to the business. Pore through Google Analytics logs. Piece together a story of the kind of people who use your application, and how they use it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Take notes on problem areas.&lt;/strong&gt; Talk to customer support about the most common questions they get&amp;ndash; they know more about pain points than anyone else in the company. Talk to the ops engineers who were on call during the last production incident. These problems are what you&amp;rsquo;ll need to test later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Take notes on what you&amp;rsquo;ve learned.&lt;/strong&gt; If you don&amp;rsquo;t like learning, you&amp;rsquo;re in the wrong field. Document everything, even things you think you&amp;rsquo;ll never forget, because one day, you WILL forget.&lt;/p&gt;
&lt;p&gt;Taking notes, not tools, is the foundation every testing career is built on.&lt;/p&gt;</description></item><item><title>Non-Lazy DMs use Obsidian for D&amp;D</title><link>https://nicolevanderhoeven.com/blog/20210930-non-lazy-dms-use-obsidian-for-dnd/</link><pubDate>Thu, 30 Sep 2021 16:29:54 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210930-non-lazy-dms-use-obsidian-for-dnd/</guid><description>&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/non-lazy-dms.PNG" alt="Graph view in Obsidian of my three D&amp;amp;D campaigns" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Last month, I wrote about how I use &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt; for &lt;a href="https://nicolevanderhoeven.com/blog/20210809-dnd-obsidian-player/"&gt;D&amp;amp;D as a player&lt;/a&gt; in a 4e campaign (I play a half-elf hybrid wizard/cleric). This is about my second weekly game, in which I am the Dungeon Master for a 5e campaign. I&amp;rsquo;m a relatively new DM (I&amp;rsquo;ve got &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/World"&gt;39 games&lt;/a&gt; under my belt at the time this writing, and Obsidian is at the center of my workflow for improving my DMing skills, getting inspiration, preparing for a game, running the game, and making sure my players are having fun.&lt;/p&gt;
&lt;p&gt;In many ways, my setup is the Non-Lazy DM&amp;rsquo;s approach to preparing for D&amp;amp;D games: high-effort, high-touch, and high-return. But I only do it because I enjoy it!&lt;/p&gt;
&lt;h2 id="plugins-i-use"&gt;Plugins I use&lt;/h2&gt;
&lt;p&gt;This is going to be a long article, so if you&amp;rsquo;re just after the plugins I use, here they are up front!&lt;/p&gt;
&lt;p&gt;I go into detail for some, but not all of these, in the article below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jeremy Valentine&amp;rsquo;s stuff
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/valentine195/obsidian-5e-statblocks"&gt;5e statblocks plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/valentine195/obsidian-initiative-tracker"&gt;Initiative tracker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/valentine195/obsidian-dice-roller"&gt;Obsidian Dice Roller&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/valentine195/obsidian-admonition"&gt;Obsidian Admonition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/valentine195/obsidian-leaflet-plugin"&gt;Obsidian Leaflet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tgrosinger/advanced-tables-obsidian"&gt;Advanced Tables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Darakah/obsidian-gallery"&gt;Gallery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vslinko/obsidian-outliner"&gt;Outliner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/blacksmithgu/obsidian-dataview"&gt;Dataview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Darakah/obsidian-timelines"&gt;Obsidian Timelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, feel free to skip around to different sections by clicking on the hamburger menu on the upper right-hand corner and selecting the session that interests you.&lt;/p&gt;
&lt;h2 id="outside-the-game"&gt;Outside the game&lt;/h2&gt;
&lt;p&gt;Although I use Obsidian &lt;em&gt;while&lt;/em&gt; playing D&amp;amp;D, I use it most for D&amp;amp;D between games: fleshing out the world, getting inspiration for new adventures, or preparing for specific sessions.&lt;/p&gt;
&lt;h3 id="world-building"&gt;World-building&lt;/h3&gt;
&lt;p&gt;Many DMs run published campaigns, but I&amp;rsquo;m not one of them. I don&amp;rsquo;t think there&amp;rsquo;s anything wrong with running published campaigns; I just personally enjoy world-building and being able to tailor the environment to what I know my specific players will like. However, I still do read campaign books, for D&amp;amp;D as well as for other systems, for inspiration. I am also a voracious fantasy reader, and consider everything fair game for &amp;ldquo;stealing&amp;rdquo; for my games.&lt;/p&gt;
&lt;p&gt;One thing I&amp;rsquo;ve begun experimenting with is processing my fiction reads the way I do my nonfiction reads. What better place to look for inspiration about creating a fantasy world than other fantasy worlds? How could I use a &lt;em&gt;Bene Gesserit&lt;/em&gt;-like faction in my campaign?&lt;/p&gt;
&lt;p&gt;I recently reread the first &lt;em&gt;Dune&lt;/em&gt; book in preparation for the movie coming out later this year, but while I was reading it, I remembered how much I love the world it&amp;rsquo;s set in. I read books on Kindle, either on my actual physical Kindle ereader or on the Kindle app on my mobile, and I took highlights while I was reading &lt;em&gt;Dune&lt;/em&gt;. You can see my full highlights &lt;a href="https://notes.nicolevanderhoeven.com/Readwise/Books/Dune"&gt;here&lt;/a&gt;, synced &lt;a href="https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux/"&gt;via Readwise&lt;/a&gt;. I highlight turns of phrases I like, character descriptions that sounded cool, or passages that are particularly revealing of the type of world the story of Dune is set in. Below is an example of a quote I highlighted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Schools were started to train human talents.” “Bene Gesserit schools?” She nodded. “We have two chief survivors of those ancient schools: the Bene Gesserit and the Spacing Guild. The Guild, so we think, emphasizes almost pure mathematics. Bene Gesserit performs another function.” “Politics,” he said. (&lt;a href="https://readwise.io/to_kindle?action=open&amp;amp;asin=B001F0WXY0&amp;amp;location=248"&gt;Location 248&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It really resonated with me as a good characterization of two important factions in the Dune universe.&lt;/p&gt;
&lt;p&gt;Then, just as I do with nonfiction books, I create a separate note about the book itself, attempting to summarize the parts of it that might be interesting or useful to me in the future. Below is a screenshot of my page on the first book of Dune. I identify &amp;ldquo;Key truths&amp;rdquo; about the world, because I also like to use that terminology when creating secrets or themes for a fantasy world or city I&amp;rsquo;ve created.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/dune-processed.png" alt="My notes on the book Dune" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://notes.nicolevanderhoeven.com/Book/Dune"&gt;Here are my full notes on Dune&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then, I also think about tropes or patterns of character, plot, or setting present in the book. I&amp;rsquo;ve started collecting these in a a page called &lt;a href="https://notes.nicolevanderhoeven.com/Forms"&gt;Forms&lt;/a&gt; in my vault. I think it&amp;rsquo;s useful to spot things that occur again and again in fantasy fiction, whether they&amp;rsquo;re overused or not. If they&amp;rsquo;re tropes that I&amp;rsquo;d rather not repeat, having a &amp;ldquo;formula&amp;rdquo; of the trope helps me either avoid it or consciously subvert it in my games. Sometimes, I just take a form wholesale and incorporate it as is.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://notes.nicolevanderhoeven.com/Form/Nuns+who+think+and+fight"&gt;Here&amp;rsquo;s an example of a page&lt;/a&gt; I created that was inspired by the Bene Gesserit&amp;ndash; a short note about &amp;ldquo;Nuns who think and fight&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t been doing it for very long, but it&amp;rsquo;s already come in useful when thinking of major events and players in my world.&lt;/p&gt;
&lt;h3 id="adventure-creation"&gt;Adventure creation&lt;/h3&gt;
&lt;p&gt;Adventures are smaller-scale and higher-resolution than campaigns or worlds, so I often need more specific notes than forms.&lt;/p&gt;
&lt;p&gt;I read and highlight adventure blog posts and articles using &lt;a href="https://blog.readwise.io/readwise-reading-app/"&gt;Readwise&amp;rsquo;s recently announced Reader app&lt;/a&gt;, and then they, along with tweets, books, and PDFs, get sent to Readwise and then Obsidian using the &lt;a href="https://help.readwise.io/article/125-how-does-the-readwise-to-obsidian-export-integration-work"&gt;Official Readwise plugin for Obsidian&lt;/a&gt;. I&amp;rsquo;ve &lt;a href="https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux/"&gt;written about this process in depth&lt;/a&gt;, but here&amp;rsquo;s what that looks like for Mike Shea&amp;rsquo;s &lt;em&gt;Return of the Lazy Dungeon Master&lt;/em&gt; - &lt;a href="https://amzn.to/2X93rvX"&gt;affiliate link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/return_of_the_lazy_dm.png" alt="My notes on Return of the Lazy Dungeon Master" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;An advantage of using Reader (which is currently in a private beta, so I&amp;rsquo;m not allowed to share screenshots of it) is that I can even highlight text locked behind a paywall. For example, I have a D&amp;amp;D Beyond subscription and I&amp;rsquo;ve bought all the books available on their store, but most reading apps don&amp;rsquo;t know that, so it&amp;rsquo;s been hard to find one that will let me highlight text from one of the WotC sourcebooks. Not so with Reader. Here&amp;rsquo;s what my highlights from the first chapter of &lt;em&gt;Volo&amp;rsquo;s Guide to Monsters&lt;/em&gt; looks like in my Obsidian vault after it&amp;rsquo;s synced from Reader:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/volos_guide-chapter1.png" alt="My highlights from Volo’s Guide to Monsters" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Readwise is great because I can highlight something on my Kindle and worry about processing it later in Obsidian. I process incoming Obsidian notes daily from 9-10pm&amp;ndash; that doesn&amp;rsquo;t mean I get through them all, just that I have a scheduled time to go through highlights and notes every day.&lt;/p&gt;
&lt;p&gt;For D&amp;amp;D-specific notes, that also involves summarizing what I&amp;rsquo;ve learned into the notes I already have on how to be a dungeon master:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/dm_session_prep.png" alt="My notes on session prep as a DM" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The referencing feature on Obsidian lets me confidently paraphrase without stealing. All I need to do is put a &lt;code&gt;[^lazydm]&lt;/code&gt; where I want the superscript to be within the text, and then at the end of the note, I put something like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[^lazydm]: Shea, M. (2018). _Return of the lazy dungeon master_. [[Return of the Lazy Dungeon Master|My highlights]].&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That gets parsed as a footnote by Obsidian and lets me properly credit authors.&lt;/p&gt;
&lt;p&gt;When I find good tools that help me flesh out adventures, I try to update &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/DM+Tools+and+Inspiration"&gt;my page on DM tools and inspiration&lt;/a&gt;, so they&amp;rsquo;re all in one place.&lt;/p&gt;
&lt;p&gt;While creating adventures, I like to use &lt;a href="https://github.com/valentine195/obsidian-admonition"&gt;Obsidian Admonition&lt;/a&gt; to call out specific parts of the text for me as I&amp;rsquo;m running the adventure, like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-admonition-example.png" alt="Sample of how I use Obsidian Admonition" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h4 id="random-tables"&gt;Random tables&lt;/h4&gt;
&lt;p&gt;I definitely have overarching plans about the wider plot in the world at large, but I also like to be surprised. Throwing random elements into games is one of the ways that I can keep myself on my toes and remind myself that I&amp;rsquo;m not the only one making the story.&lt;/p&gt;
&lt;p&gt;There are many random tables in both official and unofficial D&amp;amp;D sourcebooks, but I only use those as starting points. For me, creating random tables that are tailored to my characters and to the things they care about is where it&amp;rsquo;s at!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve experimented with creating my own random generators for various things, as well as using existing ones like &lt;a href="https://donjon.bin.sh/"&gt;Donjon&lt;/a&gt;, but it&amp;rsquo;s an Obsidian plugin, Jeremy Valentine&amp;rsquo;s &lt;a href="https://github.com/valentine195/obsidian-dice-roller"&gt;Dice Roller&lt;/a&gt;, that I have settled on.&lt;/p&gt;
&lt;p&gt;The name &amp;ldquo;Dice Roller&amp;rdquo; makes you think it&amp;rsquo;s only for rolling dice, but it&amp;rsquo;s much more than that. Its REAL value, in my opinion, is in how it lets me create tables for ANYTHING, and then randomly pull one row out of it. Here&amp;rsquo;s how it works.&lt;/p&gt;
&lt;p&gt;First, you create a table. I had a session where my players discovered a place I called &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/Shipwreck+Beach"&gt;Shipwreck Beach&lt;/a&gt;, and had a lot of fun going through several of the ships that were there. Here&amp;rsquo;s a table of &amp;ldquo;encounters&amp;rdquo; I put together for that session.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;### Ship encounters
| Roll | Encounter |
| ---- | --------------------------------------------------------------------- |
| 1 | Carnivorous Ship, ToB II p. 56 |
| 2 | Map Mimic: https://www.dndbeyond.com/monsters/1624929-map-mimic |
| 3 | Random curse: p.249 of GM&amp;#39;s Book of Random Encounters - d100 |
| 4 | Clockwork weaving spider (1, ToB p. 66) |
| 5 | 1d6 Cobbleswarm (2, ToB p. 68) |
| 6 | a feral little girl wearing a periapt of proof against poison |
| 7 | A large gold coin bearing the crest of the Kryn Dynasty and the queen |
| 8 | Book about the Arms of the Betrayer Gods |
^ship
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the &lt;code&gt;^ship&lt;/code&gt; identifier at the bottom there&amp;ndash; it can be called whatever you want, but you&amp;rsquo;ll use that identifier when rolling on that table. I also included the &lt;code&gt;Roll&lt;/code&gt; column, in case I decided on the spot to let players roll instead of doing it myself.&lt;/p&gt;
&lt;p&gt;Next, in another note (or the same one, if you want), include this line:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Shipwreck encounter: `dice: [[Shipwreck Beach#^ship]]`
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you flip to Preview mode, you&amp;rsquo;ll see this in the place of the line above:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/shipwreck_encounter-example.png" alt="Random Shipwreck encounter using Obsidian Dice Roller" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it!! This has made a HUGE impact on my games, because it&amp;rsquo;s so easy to create or modify tables. I use random encounter tables sparingly &lt;em&gt;during&lt;/em&gt; the game, but often use it &lt;em&gt;before&lt;/em&gt; a game, to introduce something fresh that I hadn&amp;rsquo;t thought of.&lt;/p&gt;
&lt;h3 id="session-prep"&gt;Session prep&lt;/h3&gt;
&lt;p&gt;As you might have guessed, I mostly use Sly Flourish&amp;rsquo;s Lazy DM steps for encounter creation, saved as an Obsidian template:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;---
type: session
world: Exandria
campaign: &amp;#34;The Slayers Five&amp;#34;
date: {{date}}
game_date: 836-03-
location: Rumblecusp
characters: [Boris, Lira, Ora, Jamedi, Flint]
tags: [TVZ]
---
## [[Session Summary]]:
^summary
---
## Housekeeping
## Recap
![[^summary]]
## Strong start
&amp;gt;
## Scenes
- [ ]
- [ ]
- [ ]
- [ ]
## Secrets and Clues
- [ ]
- [ ]
- [ ]
- [ ]
- [ ]
- [ ]
- [ ]
- [ ]
- [ ]
- [ ]
## [[Loot]]
- [ ]
---
## Log
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Before each session, I&amp;rsquo;ll go through each of these sections.&lt;/p&gt;
&lt;p&gt;I add &lt;em&gt;metadata&lt;/em&gt; to every session for use with the &lt;a href="https://github.com/blacksmithgu/obsidian-dataview"&gt;Dataview&lt;/a&gt; plugin later, in case I want to do a search for all sessions that involved a particular NPC or that took place in a certain location, for example.&lt;/p&gt;
&lt;p&gt;I fill out the &lt;em&gt;Session Summary&lt;/em&gt; section &lt;em&gt;after&lt;/em&gt; the game, but I put it at the top so I can immediately see what happened in the session in case I need to refresh my memory on it in the future. I also identify this section with a &lt;code&gt;^summary&lt;/code&gt; so that I can embed it in the next session, as a recap - more on that below.&lt;/p&gt;
&lt;p&gt;In the &lt;em&gt;Housekeeping&lt;/em&gt; section, I write down things I want to mention before we dive into the narrative of the session. Usually, these topics are scheduling- or rules-related. For example, if my players have just levelled up and I see that one of them has chosen a feat, I might make a note of it here to make sure that they know what it means. My players are pretty new, so I think it&amp;rsquo;s better to err on the side of more communication rather than less.&lt;/p&gt;
&lt;p&gt;In the &lt;em&gt;Recap&lt;/em&gt; section, I call the sumary from the previous session and embed it into the current session using something like &lt;code&gt;![[38_8360310-8360311 Seduction at the Moonlight Chateau#^summary]]&lt;/code&gt;. When in preview mode, Obsidian shows just that section labelled with &lt;code&gt;^summary&lt;/code&gt; from the previous game. I like to start the game by either reading out the summary myself, or asking one of the players to recount what they remember. That way we&amp;rsquo;re all on the same page when we begin.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;Strong start&lt;/em&gt;, I write a paragraph or two of prepared text that I use to kick off the story. This is usually the only text I&amp;rsquo;ll read out word for word during the game.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Scenes&lt;/em&gt; are a list of events that I THINK will happen. My players frequently surprise me, which I welcome, so I also list these with checkboxes so that I can keep track of where the story deviated from the plan.&lt;/p&gt;
&lt;p&gt;Sly Flourish does recommend a separate section for Fantastic Locations, but I tend to prepare these as separate pages. For example, here are my notes on a possible location, &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/The+Charred+Circus"&gt;The Charred Circus&lt;/a&gt;. I don&amp;rsquo;t include them within the session note itself, but I do make sure I have a few that characters could go to. Then, I link to all of them from the note I have for the city they&amp;rsquo;re in, &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/Port+Damali#Geography+and+Map"&gt;like this one for Port Damali&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Secrets and Clues&lt;/em&gt; is where I put a list of 10 things that players MIGHT find out somehow. This is where I look when a player rolls a natural 20 on a Perception check. The idea is to describe &lt;em&gt;what&lt;/em&gt; the secrets are, but not get too caught up in trying to plan &lt;em&gt;when&lt;/em&gt; or &lt;em&gt;how&lt;/em&gt; the players will find out those secrets. I try to include things that might be interesting for the players, as well as a few potential adventure hooks. &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/38_8360310-8360311+Seduction+at+the+Moonlight+Chateau#Secrets+and+Clues"&gt;Here&amp;rsquo;s a list that I made for a previous game&lt;/a&gt;. I have never used all 10 up in one game, but I like having more than I need.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;Loot&lt;/em&gt;, I put in any items I know the party will likely get to. If they&amp;rsquo;re in a boss fight, I usually save in this section all the things I&amp;rsquo;ve decided beforehand they&amp;rsquo;ll get. If I don&amp;rsquo;t have something specific like that in mind, I sometimes randomize a bunch of non-magical trinkets. The &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/Loot"&gt;Loot page&lt;/a&gt; contains items I&amp;rsquo;ve picked out for specific characters.&lt;/p&gt;
&lt;h3 id="post-session-processing"&gt;Post-session processing&lt;/h3&gt;
&lt;p&gt;If your players are anything like mine, you&amp;rsquo;ll often find yourself with a bunch of unused material and a lot of stuff you still had to improvise on the spot, despite adequate preparation. That&amp;rsquo;s okay! I actually enjoy when my players do something unexpected. I encourage and reward creative play&amp;hellip; but it does mean that I have to do a bit of a debrief after each session.&lt;/p&gt;
&lt;p&gt;I try not to prepare things that may really be &amp;ldquo;wasted&amp;rdquo;. Things like locations, NPCs, or general plot hooks can usually be recycled. After games, I spend some time tying off loose ends, noting down decisions the party made and the reasoning behind those decisions, and tweaking connections between NPCs or places based on how the party reacted.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fleshing out bits of the world, with each city or NPC getting a page&lt;/li&gt;
&lt;li&gt;Tagging cities and NPCs with Location or NPC, and then within the city, doing a query for NPC and &lt;code&gt;city name&lt;/code&gt; to get everyone living in that city.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I use metadata for every page in my campaign so that I can use the excellent &lt;a href="https://github.com/blacksmithgu/obsidian-dataview/"&gt;Dataview plugin&lt;/a&gt; later to find whatever I want. You already saw my session template, but here&amp;rsquo;s an NPC template that I use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;NPC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;faction&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;location&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;world&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Exandria&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;campaign&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;The Slayers Five&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;date&lt;/span&gt;: [{{&lt;span style="color:#ae81ff"&gt;date}}]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;description&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;race&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;gender&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;class&lt;/span&gt;: []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I insert this template for every single NPC in my world, whether my players get to it or not. Because I use it so often, I&amp;rsquo;ve set up a hotkey for inserting templates in the Obsidian settings:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-inserting_template.png" alt="Setting a hotkey for template insertion" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;That way, all I have to do is hit &lt;code&gt;CMD + ;&lt;/code&gt; and select the right template from the list, and the text above is inserted at the beginning of the note as frontmatter, with all the fields I need to fill out. The date is also automatically generated.&lt;/p&gt;
&lt;p&gt;Using the Dataview plugin, I can then embed queries on location pages so that I can see NPCs in that place at a glance. For example, here&amp;rsquo;s the snippet I put under the &lt;code&gt;People&lt;/code&gt; section in my Port Damali page:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;&lt;span style="color:#f92672"&gt;```&lt;/span&gt;dataview
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;table&lt;/span&gt; shop &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; Shop, faction &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; Faction, description &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; Description &lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Exandria&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;contains&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;NPC&amp;#34;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;and&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;contains&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;location&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;Port Damali&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SORT file.name &lt;span style="color:#66d9ef"&gt;ASC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;&lt;span style="color:#f92672"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(If you try this, remove the backticks &lt;code&gt;\&lt;/code&gt; in the snippet above&amp;ndash; those are just to show you the syntax for a dataview query.)&lt;/p&gt;
&lt;p&gt;That query, when rendered in Preview mode in Obsidian, looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-dataview-port_damali.png" alt="Rendered Dataview query in Obsidian Preview mode" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Having the metadata for every NPC, monster, item, and place in my world is essential for creating queries like this. I&amp;rsquo;ve also found it useful to maintain a model of the metadata I use:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;- `type`
- `NPC`
- `PC`
- `shop`
- `place`
- `item`
- `session` for D&amp;amp;D play sessions
- `quest`
- `race`
- `faction`
- `family`
- `monster`
- `condition`
- `deity`
- `other` for anything else
- `characters` (for sessions): characters involved in this session
- `deceased`
- `true`
- `false`
- `family`
- `faction`
- `race`
- `gender`
- `class`
- `description`
- `date`
- `game_date`
- `world`
- `Exandria`
- `Forgotten Realms`
- `Exandya`
- `campaign`
- `Order of the Wandering Mind`
- `The Slayers Five`
- `Die vergessene Schwester`
- `Temporary White Circle`
- `shop` (for proprietors)
- `shop_type` (for shops)
- `general` : general goods store
- `alchemy`: potions
- `magical items`: sells magical items
- `services`: magical spells, messaging, brothel
- `tavern`: food and drink
- `inn`: lodging
- `herbalism`
- `smithy`
- `location`: city of residence
- `origin`: city of origin
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="during-the-game"&gt;During the game&lt;/h2&gt;
&lt;p&gt;I keep a &amp;ldquo;World&amp;rdquo; page for each of my D&amp;amp;D campaigns, including a list of sessions:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/campaign-page.png" alt="Campaign Page with list of sessions" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;During the game, I use the session page that I mentioned above, and start filling in the &lt;em&gt;Log&lt;/em&gt; section. I tend to take way fewer notes as a DM than as a player, just because I&amp;rsquo;m busy doing other things, but I do always write down things that the players do that I might need to remember later.&lt;/p&gt;
&lt;p&gt;One thing that comes up frequently in my games is the need to improvise encounters, NPCs, or locations. I&amp;rsquo;ve had great success in creating a &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/Random+Rolls"&gt;Random Rolls&lt;/a&gt; page that collates many of these random tables in one note. If you click on that link, you unfortunately won&amp;rsquo;t be able to see the results rendered, because the Dice Roller plugin isn&amp;rsquo;t available for Obsidian Publish, but maybe you&amp;rsquo;ll get an idea of things I&amp;rsquo;m rolling on. Most of the tables referred to are from &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/Random+Tables"&gt;this Random Tables note&lt;/a&gt;, but every now and then I&amp;rsquo;ll include tables in location notes instead.&lt;/p&gt;
&lt;p&gt;I always have this Random Rolls page up as part of my DM workspace, in case I need to quickly improvise something. Here&amp;rsquo;s what the NPC section looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/npc_dice_roller-example.png" alt="Random rolls on the NPC random tables" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I can use any or all of those things that I want, or use them as inspiration for something else I make up on the spot. Either way, it means I&amp;rsquo;ll never be completely at a loss for the name of an NPC. ;)&lt;/p&gt;
&lt;h3 id="initiative-tracker"&gt;Initiative Tracker&lt;/h3&gt;
&lt;p&gt;I use D&amp;amp;D Beyond for character sheets, but I use &lt;a href="https://github.com/valentine195/obsidian-initiative-tracker"&gt;Jeremy Valentine&amp;rsquo;s initiative tracker&lt;/a&gt; to handle combat because there are a few critical features that D&amp;amp;D Beyond&amp;rsquo;s Encounter Tracker doesn&amp;rsquo;t yet support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Status conditions&lt;/li&gt;
&lt;li&gt;(Easy to tweak) homebrew monsters&lt;/li&gt;
&lt;li&gt;Hiding monsters&amp;rsquo; names from the players&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Initiative Tracker plugin supports all of these. When activated, it appears as a side panel in Obsidian:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/initiative_tracker_plugin.png" alt="Jeremy Valentine’s initiative tracker for Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I can easily see everyone&amp;rsquo;s HP and AC at a glance, and also apply special status conditions when necessary. My players don&amp;rsquo;t have to know whose turn it is&amp;ndash; they just have to know when their turn is. I warn them verbally when they&amp;rsquo;re &amp;ldquo;on deck&amp;rdquo;, and they haven&amp;rsquo;t had an issue with that.&lt;/p&gt;
&lt;p&gt;D&amp;amp;D Beyond does allow you to create homebrew monsters, but the only real benefit to doing so is to be able to use it in their Combat Tracker, so I&amp;rsquo;ve been using another plugin by Jeremy Valentine, the author of this one, to create monster statblocks&lt;/p&gt;
&lt;h3 id="statblocks"&gt;Statblocks&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/valentine195/obsidian-5e-statblocks"&gt;The 5e statblocks plugin&lt;/a&gt; lets me easily create homebrew creatures just by using this format:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;\```statblock
name: string
size: string
type: string
subtype: string
alignment: string
ac: number
hp: number
hit_dice: string
speed: string
stats: [number, number, number, number, number, number]
fage_stats: [number, number, number, number, number, number, number, number, number]
saves:
- &amp;lt;ability-score&amp;gt;: number
skillsaves:
- &amp;lt;skill-name&amp;gt;: number
damage_vulnerabilities: string
damage_resistances: string
damage_immunities: string
condition_immunities: string
senses: string
languages: string
cr: number
spells:
- &amp;lt;description&amp;gt;
- &amp;lt;spell level&amp;gt;: &amp;lt;spell-list&amp;gt;
traits:
- [&amp;lt;trait-name&amp;gt;, &amp;lt;trait-description&amp;gt;]
- ...
actions:
- [&amp;lt;trait-name&amp;gt;, &amp;lt;trait-description&amp;gt;]
- ...
legendary_actions:
- [&amp;lt;legendary_actions-name&amp;gt;, &amp;lt;legendary_actions-description&amp;gt;]
- ...
reactions:
- [&amp;lt;reaction-name&amp;gt;, &amp;lt;reaction-description&amp;gt;]
- ...
\```
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;(Again, remove the backticks &lt;code&gt;\&lt;/code&gt; at the starting and ending lines of the snippet above if you copy and paste this into your vault.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I usually start with a &amp;ldquo;real&amp;rdquo; monster from official or unofficial sourcebooks, then tweak it from there. For example, here&amp;rsquo;s a sample one that I tweaked from &lt;a href="https://koboldpress.com/tome-of-beasts/"&gt;Tome of Beasts&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-statblocks-example.png" alt="Sample of 5e statblock plugin in action" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The best part about this plugin is that it integrates with the Initiative Tracker. When you create a statblock and save it, that monster then becomes available for use within the Initiative Tracker, as you can see from the screenshot above.&lt;/p&gt;
&lt;h2 id="publishing"&gt;Publishing&lt;/h2&gt;
&lt;p&gt;I subscribe to &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/World"&gt;Obsidian Publish&lt;/a&gt;, partly to give back to the fine Obsidian developers who&amp;rsquo;ve made a tool I use daily and partly because I&amp;rsquo;ve found that it&amp;rsquo;s reduced friction in publishing. I use it primarily for non-D&amp;amp;D-related things, but D&amp;amp;D is part of my life, so it&amp;rsquo;s there as well. &lt;a href="https://notes.nicolevanderhoeven.com/Exandria/World"&gt;You can find my notes on the D&amp;amp;D campaign I DM here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some of the plugins that I use unfortunately don&amp;rsquo;t render correctly when Published, but the developers are working on that.&lt;/p&gt;
&lt;p&gt;I publish my campaign notes mainly because I love seeing other DM&amp;rsquo;s thought processes and systems, so I thought I&amp;rsquo;d return the favour by sharing mine as well. In case you were wondering, my players don&amp;rsquo;t know I publish these notes&amp;ndash; or, at least, I hope they don&amp;rsquo;t!&lt;/p&gt;
&lt;h2 id="player-facing-campaign-vault"&gt;Player-facing campaign vault&lt;/h2&gt;
&lt;p&gt;I used to drop handouts into a WhatsApp group with my players, but it became increasingly unwieldy to look for something. We switched to Slack, but that had the same problem of needing to scroll back through lots of photos before finding the right one. Photos aren&amp;rsquo;t very searchable in those apps.&lt;/p&gt;
&lt;p&gt;A few months ago, I switched to Obsidian for handouts. I created a separate vault for the campaign and shared it with my players via Dropbox. I did have to talk them through the setup, but it&amp;rsquo;s worked out very well for us as a free alternative.&lt;/p&gt;
&lt;p&gt;In the vault, I put things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pictures and other handouts&lt;/li&gt;
&lt;li&gt;Session videos and summaries&lt;/li&gt;
&lt;li&gt;Links for everything we use as a group&lt;/li&gt;
&lt;li&gt;House rules&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="session-page"&gt;Session page&lt;/h3&gt;
&lt;p&gt;I create a page for every session we have, which has a section for the video recording of that session (unlisted and just for us, sorry), a player-friendly summary, and a log. The log is where I dump photos I give them during the session. Here&amp;rsquo;s what that looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-player-vault-session.png" alt="Session page for my player-facing vault" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t put any other notes in the log; that&amp;rsquo;s the players&amp;rsquo; jobs (if they want to).&lt;/p&gt;
&lt;p&gt;I like to have a &amp;ldquo;Story Thus Far&amp;rdquo; page that embeds the video and summary sections of all the sessions, so any of us can read through it to remember what&amp;rsquo;s happened. It&amp;rsquo;s fun to see how far we&amp;rsquo;ve come as a group.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-player_vault-story-thus-far.png" alt="Story Thus Far page in my player-facing vault" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="about-our-game"&gt;About our game&lt;/h3&gt;
&lt;p&gt;The About our game page has a list of links to all the tools we use, our schedule, and a few things about the campaign and the world.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-player_vault-about_our_game.png" alt="About our game page in my player-facing vault" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="rules"&gt;Rules&lt;/h3&gt;
&lt;p&gt;I keep a house rules page that shows all the extra rules I&amp;rsquo;ve put into the game. I mostly play with all the WoTC-produced sourcebooks, but I&amp;rsquo;ve added some extra rules about things that my players are interested in, such as strongholds (for which I use &lt;a href="https://shop.mcdmproductions.com/collections/strongholds-followers-books"&gt;Matt Colville&amp;rsquo;s Strongholds &amp;amp; Followers book&lt;/a&gt;) and smith&amp;rsquo;s tools.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-player_vault-rules.png" alt="Rules page in my player-facing vault" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I also have some other pages on rules that come up often, such as Ship statblocks (since the campaign is set on a coast) and suggested downtime activities.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I call my approach to game preparation the Non-Lazy DM&amp;rsquo;s approach as a tongue-in-cheek nod to &lt;a href="https://slyflourish.com"&gt;Mike Shea&amp;rsquo;s&lt;/a&gt; content, but there&amp;rsquo;s truth to the moniker, too. My setup is extremely detail-oriented, time-consuming, and &lt;em&gt;fun&lt;/em&gt;&amp;hellip; at least for me! And that&amp;rsquo;s the key: I wouldn&amp;rsquo;t be doing any of this if I didn&amp;rsquo;t find it truly enjoyable to world-build to this level of detail.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a non-lazy DM too, thanks for sticking around! I hope this has given you some ideas for how you could use Obsidian to run your games and stimulate your imagination, too.&lt;/p&gt;</description></item><item><title>How to import Shortform highlights to Obsidian via Readwise</title><link>https://nicolevanderhoeven.com/blog/20210815-shortform-to-readwise/</link><pubDate>Mon, 16 Aug 2021 21:40:16 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210815-shortform-to-readwise/</guid><description>
&lt;meta property=“og:image” content="/assets/20210816-shortform-to-readwise-title.png.png" /&gt;
&lt;meta property=”og:image:width” content=”1200″ /&gt;
&lt;meta property=”og:image:height” content=”628″ /&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210816-shortform-to-readwise-title.png" alt="Shortform to Readwise hero photo" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always been dubious of those book summary apps. You know the ones&amp;ndash; they tend to have obnoxious ads that promise to help you &amp;ldquo;read 5 books a day&amp;rdquo; or learn the world&amp;rsquo;s secrets in 15 minutes. Having tried a few of them, I&amp;rsquo;ve been disappointed with the quality of their summaries (or lack thereof). They attempt to distill the message of a 300+ page book into a handful of bullet points, which I&amp;rsquo;ve found neither accurate nor useful. Recently, though, I found myself happily subscribing to one, and here&amp;rsquo;s why.&lt;/p&gt;
&lt;h2 id="whats-shortform-and-why-is-it-awesome"&gt;What&amp;rsquo;s Shortform and why is it awesome?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://shortform.com/nicole"&gt;Shortform&lt;/a&gt; really took me aback because it isn&amp;rsquo;t just a book summary. I should mention here that I&amp;rsquo;m not affiliated with Shortform and gain nothing by recommending them. I&amp;rsquo;m just a happy subscriber.&lt;/p&gt;
&lt;p&gt;When I read their guide for the book &lt;em&gt;Why We Sleep&lt;/em&gt;, I was pleasantly surprised to find that Shortform not only summarized it succinctly, but they also added intelligent counterarguments to the author&amp;rsquo;s points. In some cases, when the author made a false claim, there were Shortform notes that linked to studies that showed &lt;em&gt;why&lt;/em&gt; the claim was false. In other cases, Shortform mentioned related concepts proposed in other books that strengthened the author&amp;rsquo;s viewpoint. I found myself looking up their guides of books I&amp;rsquo;d already read, just because I was curious to see if I had missed anything. And I had!&lt;/p&gt;
&lt;p&gt;In short, Shortform is the only book summary reader I&amp;rsquo;ve found that goes beyond summarizing and actually &lt;em&gt;adds value&lt;/em&gt; to the reading experience, &lt;em&gt;even if you&amp;rsquo;ve read the book.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;They also have a one-page summary of the book AND a summary per chapter, so information is presented at different levels of detail. In my time with it, my most frequent workflow with it has involved reading the one-page summary of a book, buying it if it was interestering enough, reading the book, and then going back to Shortform to go read the chapter summaries. And both the one-page and detailed chapter summaries, including Shortform notes, can be highlighted!&lt;/p&gt;
&lt;p&gt;Naturally I wanted to take it further than that. I already &lt;a href="https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux"&gt;sync my Kindle highlights to Obsidian&lt;/a&gt; automatically and incrementally process them in Obsidian, so I wrote a script to have my Shortform highlights going to &lt;a href="https://readwise.io"&gt;Readwise&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210816-shortform-01.png" alt="Shortform highlights in my Obsidian vault" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My Shortform highlights for the book Mindset, in my Obsidian vault.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The screenshot above shows the final result: highlights I made in the Shortform web or mobile app that have been synced to a page in my Obsidian vault. Here&amp;rsquo;s how I did that.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: Since this article was published, Shortform and Readwise have created an integration, so the script I mention here is no longer necessary. Click &lt;a href="https://nicolevanderhoeven.com/blog/20221021-obsidian-for-non-coders"&gt;here&lt;/a&gt; to see what I&amp;rsquo;d recommend doing today.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="what-you-need"&gt;What you need&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://shortform.com/nicole"&gt;Shortform&lt;/a&gt; account. &lt;em&gt;(affiliate link)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://readwise.io/nicole"&gt;Readwise&lt;/a&gt; account. &lt;em&gt;(This is an affiliate link. If you sign up with this link, you&amp;rsquo;ll get a two-month free trial instead of one, and I get a small percentage if you sign up for a plan.)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;A tool that Readwise can export to, such as &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;, &lt;a href="https://roamresearch.com"&gt;Roam Research&lt;/a&gt;, &lt;a href="https://notion.so"&gt;Notion&lt;/a&gt;, or &lt;a href="https://evernote.com"&gt;Evernote&lt;/a&gt;. You can also use their CSV and Markdown exports.&lt;/li&gt;
&lt;li&gt;A local installation of Python 3, which you can &lt;a href="https://www.python.org/downloads/"&gt;download here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obsidian is my Personal Knowledge Management tool of choice, and I use the &lt;a href="https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux/"&gt;Readwise Official community plugin&lt;/a&gt; to get Readwise to send highlights to Obsidian. However, using Obsidian is not necessary as my script connects only Shortform to Readwise.&lt;/p&gt;
&lt;h2 id="the-setup"&gt;The setup&lt;/h2&gt;
&lt;p&gt;Clone &lt;a href="https://github.com/nicolevanderhoeven/shortform-to-readwise"&gt;my Shortform-to-Readwise repository on GitHub&lt;/a&gt; or download it to your computer.&lt;/p&gt;
&lt;h3 id="enter-your-tokens"&gt;Enter your tokens&lt;/h3&gt;
&lt;p&gt;The file &lt;code&gt;variables.py&lt;/code&gt; requires two tokens: one from your Shortform account and another from your Readwise account:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Your authorization token for Shortform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;authToken &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Your Readwise API token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Retrieve this token from https://readwise.io/access_token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;readwiseToken &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="getting-your-shortform-token"&gt;Getting your Shortform token&lt;/h4&gt;
&lt;p&gt;This is a little hacky, because Shortform doesn&amp;rsquo;t expose an API token in their web app. So you&amp;rsquo;ll need to log in to your Shortform account and open up DevTools in your browser. In Chrome, go to Options &amp;gt; More Tools &amp;gt; Developer Tools (or, on Mac, hit CMD+OPTION+I). Click on the Network panel.&lt;/p&gt;
&lt;p&gt;You should now see Shortform on one side, and DevTools on another in the same browser window.&lt;/p&gt;
&lt;p&gt;Then, go to &lt;a href="https://www.shortform.com/app/highlights"&gt;your Highlights page&lt;/a&gt;. Doing so will populate the Network panel with a bunch of requests that look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210816-shortform-02.png" alt="DevTools open in Chrome, showing requests from Shortform" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;DevTools open in Chrome, showing requests from Shortform.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Click on the request with the name &lt;code&gt;?sort=date&lt;/code&gt;, highlighted above. That&amp;rsquo;ll open another side panel that will show Headers. Scroll down to the section marked &amp;ldquo;Request Headers&amp;rdquo;, and look for where it says &lt;code&gt;Authorization: Basic &lt;/code&gt; followed by a bunch of characters.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210816-shortform-03.png" alt="Authorization headers for Shortform, in DevTools" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Authorization headers for Shortform, in DevTools.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve removed my personal authorization token from the screenshot above, but it&amp;rsquo;s a long series of characters that comes after &lt;code&gt;Authorization: Basic &lt;/code&gt;. Go ahead and right click on it and click Copy Value. Paste it into &lt;code&gt;variables.py&lt;/code&gt; as the value for &lt;code&gt;authToken&lt;/code&gt;, and then _remove &lt;code&gt;Basic &lt;/code&gt; (including the space after it) so that you&amp;rsquo;re saving only everything that comes afterwards.&lt;/p&gt;
&lt;h4 id="getting-your-readwise-token"&gt;Getting your Readwise token&lt;/h4&gt;
&lt;p&gt;Thankfully, this one&amp;rsquo;s a little easier. Go to &lt;a href="https://readwise.io/access_token"&gt;this page on Readwise&lt;/a&gt; after logging in. Click &amp;ldquo;Get Token&amp;rdquo;, click &amp;ldquo;Copy to clipboard&amp;rdquo;, and then paste it into &lt;code&gt;variables.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Save &lt;code&gt;variables.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note: Don&amp;rsquo;t share either of these tokens with anyone else, as they will then be able to access your Shortform or Readwise accounts.&lt;/p&gt;
&lt;h3 id="run-the-script"&gt;Run the script&lt;/h3&gt;
&lt;p&gt;Open up your terminal, and &lt;code&gt;cd&lt;/code&gt; into the &lt;code&gt;/shortform-to-readwise&lt;/code&gt; repository. Then, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python3 getHighlights.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It should finish in a few seconds (depending on how many Shortform highlights you have), and then you can verify that it worked by going to &lt;a href="https://readwise.io/books"&gt;your Readwise library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210816-shortform-04.png" alt="Shortform exports showing up in your Readwise library" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Shortform exports showing up in your Readwise library.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You should see books that have the &lt;code&gt;Shortform-&lt;/code&gt; prefix. If you do, then you&amp;rsquo;re done! If you&amp;rsquo;ve got your Readwise account set up to push your highlights elsewhere (such as Obsidian), then you&amp;rsquo;re all set!&lt;/p&gt;
&lt;p&gt;In case you&amp;rsquo;re wondering why I added the &lt;code&gt;Shortform-&lt;/code&gt; prefix, it&amp;rsquo;s because I often read the Shortform guide, and then read the book itself. Rather than end up in a sticky situation where the Shortform guide highlights might override the actual book highlights, I titled it differently so that I get two pages for the two sets of highlights.&lt;/p&gt;
&lt;h2 id="what-about-running-the-script-regularly"&gt;What about running the script regularly?&lt;/h2&gt;
&lt;p&gt;The whole point of this, at least for me, was to set it up so that I&amp;rsquo;ve got highlights going to Obsidian &lt;em&gt;automatically&lt;/em&gt;, without needing my intervention. What I did was to use &lt;code&gt;crontab&lt;/code&gt; on macOS to run the script every 30 minutes, but you can set up your own schedule.&lt;/p&gt;
&lt;h3 id="give-cron-full-system-access"&gt;Give cron full system access&lt;/h3&gt;
&lt;p&gt;Cron needs to have the right permissions to run scripts on your behalf, so you may have to go to System Preferences &amp;gt; Security &amp;amp; Privacy &amp;gt; Privacy &amp;gt; Full Disk Access on your laptop and add &lt;code&gt;/usr/bin/cron&lt;/code&gt; to the &amp;ldquo;allowed&amp;rdquo; list.&lt;/p&gt;
&lt;h3 id="find-the-path-for-your-python3-installation"&gt;Find the path for your python3 installation&lt;/h3&gt;
&lt;p&gt;In your terminal, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;which python3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will give you a path. For me, it was &lt;code&gt;/Library/Frameworks/Python.framework/Versions/3.9/bin/python3&lt;/code&gt;. Copy this.&lt;/p&gt;
&lt;h3 id="open-crontab"&gt;Open crontab&lt;/h3&gt;
&lt;p&gt;Then, from your terminal, run the command &lt;code&gt;env EDITOR=nano crontab -e&lt;/code&gt; to open up a list of cronjobs. If you don&amp;rsquo;t have any other jobs, this will be blank.&lt;/p&gt;
&lt;h3 id="set-a-schedule"&gt;Set a schedule&lt;/h3&gt;
&lt;p&gt;Add a line to crontab for the schedule you want to create. This line has to be in the format&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* * * * * command
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;where the five asterisks correspond to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;minute&lt;/li&gt;
&lt;li&gt;hour&lt;/li&gt;
&lt;li&gt;day of the month&lt;/li&gt;
&lt;li&gt;month&lt;/li&gt;
&lt;li&gt;day of the week&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my case, I wanted it to run every 30 minutes, so here&amp;rsquo;s what my schedule looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*/30 * * * * /Library/Frameworks/Python.framework/Versions/3.9/bin/python3 /Users/nic/python/shortform-to-readwise/gethighlights.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make sure you change &lt;code&gt;/Users/nic/python/&lt;/code&gt; to the path where you put the repository and your python3 path instead of &lt;code&gt;/Library/Frameworks/Python.framework/Versions/3.9/bin/python3&lt;/code&gt;. Now exit out of it and save it (CTRL+X, and yes). The Python script should automatically run every 30 minutes.&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s it!&lt;/h2&gt;
&lt;p&gt;I consume a lot of content daily, but without systems in place to capture insights from that content and process it, it all just gets forgotten eventually. That&amp;rsquo;s why I love services like Shortform and Readwise&amp;ndash; they let me get what I&amp;rsquo;ve read/watched/heard out of my head and into Obsidian, where I can review them more critically and truly learn the important parts.&lt;/p&gt;
&lt;p&gt;Both are paid services (which I paid for out of my own money), but I really think they&amp;rsquo;re worth it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This post contains affiliate links. Check out &lt;a href="https://nicolevanderhoeven.com/ethics/"&gt;My Ethics Statement&lt;/a&gt; to see my standards for promotions.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Meet the Grafana Labs team: Nicole Van Der Hoeven, Developer Advocate at k6</title><link>https://nicolevanderhoeven.com/blog/20210813-grafana-profile-nicole-van-der-hoeven/</link><pubDate>Fri, 13 Aug 2021 15:29:37 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210813-grafana-profile-nicole-van-der-hoeven/</guid><description>&lt;p&gt;&lt;a href="https://grafana.com/blog/2021/08/13/meet-the-grafana-labs-team-nicole-van-der-hoeven-a-developer-advocate-who-does-performance-testing-at-k6/"&gt;Originally posted here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
&lt;link rel="canonical" href="https://grafana.com/blog/2021/08/13/meet-the-grafana-labs-team-nicole-van-der-hoeven-a-developer-advocate-who-does-performance-testing-at-k6/"&gt;
As Grafana Labs continues to grow, we’d like you to get to know the team members who are working on the cool stuff you’re using. Check out the latest of our Friday team profiles.&lt;/p&gt;
&lt;h2 id="meet-nicole"&gt;Meet Nicole!&lt;/h2&gt;
&lt;p&gt;Name: Nicole van der Hoeven&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/profilepic-1MB.png" alt="photo of Nicole" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="current-locationtime-zone"&gt;Current location/time zone&lt;/h3&gt;
&lt;p&gt;I currently live in Maastricht, in the Netherlands. I moved here with my husband four years ago from Melbourne, Australia.&lt;/p&gt;
&lt;h3 id="what-do-you-do-at-grafana-labs"&gt;What do you do at Grafana Labs?&lt;/h3&gt;
&lt;p&gt;I’m a Developer Advocate at &lt;a href="https://k6.io"&gt;k6&lt;/a&gt;, but I’m not actually a developer — I’m a performance tester! I guess there’s no such thing as a Performance Tester Advocate? My job is part engineering, part content creation: I write load testing scripts and code, but I also write blog posts, produce videos, and give presentations. I found out I was joining Grafana Labs a few months ago &lt;a href="https://grafana.com/about/press/2021-06-17-grafana-labs-brings-modern-open-source-load-testing-to-observability-with-acquisition-of-k6/"&gt;when k6 was acquired&lt;/a&gt;, but I was happy to join the team! I’ve met some fun and motivated people at Grafana Labs that I’m eager to collaborate with.&lt;/p&gt;
&lt;h3 id="what-open-source-projects-do-you-contribute-to"&gt;What open source projects do you contribute to?&lt;/h3&gt;
&lt;p&gt;I’m a budding developer and still learning the ins and outs of social coding, but some things I’ve contributed to recently are &lt;a href="https://github.com/nicolevanderhoeven/xk6-chaos"&gt;a chaos engineering extension for k6&lt;/a&gt; (created by my colleague, the talented &lt;a href="https://simme.dev"&gt;Simon Aronsson&lt;/a&gt;), an application called &lt;a href="https://github.com/nicolevanderhoeven/pokeapi"&gt;Poké API&lt;/a&gt; that I’ve added chaos experiments to, and &lt;a href="https://github.com/nicolevanderhoeven/readwise2directory"&gt;a Python script&lt;/a&gt; for pushing my Kindle highlights to my note-taking app of choice, &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="what-are-your-github-and-twitter-handles"&gt;What are your GitHub and Twitter handles?&lt;/h3&gt;
&lt;p&gt;GitHub: &lt;a href="https://github.com/nicolevanderhoeven"&gt;nicolevanderhoeven&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Twitter: &lt;a href="https://twitter.com/n_vanderhoeven"&gt;n_vanderhoeven&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="what-do-you-like-to-do-in-your-free-time"&gt;What do you like to do in your free time?&lt;/h3&gt;
&lt;p&gt;I play Dungeons &amp;amp; Dragons eight hours a week, &lt;a href="https://notes.nicolevanderhoeven.com/Fork+My+Brain"&gt;take a ridiculous amount of notes on everything&lt;/a&gt;, and vlog about &lt;a href="https://www.youtube.com/c/luckydippers"&gt;my (pre-COVID) travels&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/battlemap.jpg" alt="Battle map on a TV lying on a table, from a Dungeons &amp;amp; Dragons game" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A battle map in one of my Dungeons &amp;amp; Dragons games.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="whats-your-favorite-new-gadget-or-tech-toy"&gt;What’s your favorite new gadget or tech toy?&lt;/h3&gt;
&lt;p&gt;My new Rødecaster Pro, an audio mixer that has quickly become an essential part of my audio and video production process. It takes up a huge amount of precious real estate on my desk, but it is absolutely worth it!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/desksetup.jpg" alt="Bird’s eye view of Nicole’s current desk" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My current desk setup, including the Rødecaster Pro peeking out from underneath the laptop in the upper right.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="whats-the-best-part-of-your-job"&gt;What’s the best part of your job?&lt;/h3&gt;
&lt;p&gt;I got into Developer Relations and advocacy in general because I realized that, as a performance testing consultant, I was being asked the same questions and solving the same problems. So I started to share my experience online, and I’ve found that significantly more efficient. Why explain something repeatedly to one person at a time, on the spot, when you could post a more considered explanation online and potentially help an unlimited amount of people? A large part of my job is simplifying technical concepts and making it accessible to beginners, and I love being able to help others by creating the content I looked for when I was a beginner, too.&lt;/p&gt;
&lt;p&gt;Here’s one of my YouTube videos:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_DB-lNZW7LQ?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id="whats-the-last-thing-you-binge-watched"&gt;What’s the last thing you binge-watched?&lt;/h3&gt;
&lt;p&gt;I have binge-watched the entire two campaigns of &lt;a href="https://www.youtube.com/c/criticalrole"&gt;Critical Role&lt;/a&gt;, a livestream of a Dungeons &amp;amp; Dragons game with a bunch of voice actors, which is over 1000 hours of watch time. Totally worth it!&lt;/p&gt;
&lt;h3 id="what-superpowers-do-you-have"&gt;What superpowers do you have?&lt;/h3&gt;
&lt;p&gt;I speak several languages: Tagalog (my native language), English, Dutch, Spanish, Esperanto, German, and French — in that order and to varying degrees. I am learning and have learned more, but don’t yet feel confident enough to claim to “speak” them! I choose travel destinations based on how interested I am in learning the local language.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/esperanto-superpower.jpg" alt="Nicole wearing a shirt that says “I speak Esperanto. What’s your superpower?”" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="whats-your-zoom-virtual-background-and-why"&gt;What’s your Zoom virtual background and why?&lt;/h3&gt;
&lt;p&gt;I don’t use virtual backgrounds. The thing I like most about video is that it’s the most honest form of expression: writing, podcasting, and photography can all be edited far more easily. I like that my home office gives clues about who I am and what I like, and I would never want to hide that — mess and all!&lt;/p&gt;</description></item><item><title>The easiest way to sync Kindle highlights to Obsidian: Readwise to Obsidian Redux</title><link>https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux/</link><pubDate>Tue, 10 Aug 2021 19:50:40 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux/</guid><description>
&lt;meta property=“og:image” content="/assets/readwise-official-plugin.png" /&gt;
&lt;meta property=”og:image:width” content=”1200″ /&gt;
&lt;meta property=”og:image:height” content=”628″ /&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/readwise-official-plugin.png" alt="Readwise to Obsidian workflow" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Almost exactly six months ago, I &lt;a href="https://nicolevanderhoeven.com/blog/20210206-readwise-to-obsidian/"&gt;wrote a blog post&lt;/a&gt; detailing my workflow for processing content. Specifically, I wrote about how I used a python script to take my highlights from Instapaper, Kindle, Twitter, and Airr and automatically put them into Obsidian.&lt;/p&gt;
&lt;p&gt;That method worked, and you can read the original article in the link above. However, today, &lt;a href="https://readwise.io/nicole"&gt;Readwise&lt;/a&gt; announced that they now have &lt;a href="https://help.readwise.io/article/125-how-does-the-readwise-to-obsidian-export-integration-work"&gt;an official Obsidian community plugin&lt;/a&gt;, and I disabled my trusty old python script for good.&lt;/p&gt;
&lt;p&gt;You see, although my old script worked, there were quirks&amp;ndash; not the least of which was that making changes to it meant programming in Python. I&amp;rsquo;m not a developer, so while I made do, it was never a painless experience. I also ran into issues with time zones and invalid characters being used in titles. So I&amp;rsquo;m thrilled to have something significantly better from Readwise itself! Here&amp;rsquo;s a video from the Readwise team, or read on for instructions on how to get started.&lt;/p&gt;
&lt;p&gt;&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/DOYwwdDC9LQ?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;em&gt;From &lt;a href="https://www.youtube.com/watch?v=DOYwwdDC9LQ"&gt;Readwise on YouTube&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="install-the-obsidian-plugin"&gt;Install the Obsidian plugin.&lt;/h2&gt;
&lt;p&gt;In your Obsidian vault, go to Settings &amp;gt; Community plugins. You&amp;rsquo;ll need to turn Safe mode off for community plugins if you haven&amp;rsquo;t already.&lt;/p&gt;
&lt;p&gt;Then, click Browse and search for Readwise Official.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/readwise-official.png" alt="Screenshot of the Readwise Official plugin within Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Install it, and then enable it.&lt;/p&gt;
&lt;h2 id="connect-your-readwise-account-to-the-obsidian-plugin"&gt;Connect your Readwise account to the Obsidian plugin&lt;/h2&gt;
&lt;p&gt;Head back to the Settings menu and scroll down to the &amp;ldquo;Plugin Options&amp;rdquo; section, and then click Readwise Official.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/readwise-official-connect.png" alt="Screenshot of the initial screen of the Readwise Official plugin" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Click connect as highlighted in orange above, and a browser window will open, showing the &lt;a href="https://readwise.io/export/obsidian/preferences"&gt;Obsidian Export&lt;/a&gt; options. You&amp;rsquo;ll need to sign into your Readwise account if you haven&amp;rsquo;t already.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-export.png" alt="Obsidian export page" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;At this point, your Readwise account will have been connected to the Readwise Official plugin, but you won&amp;rsquo;t have triggered any syncs yet. If you don&amp;rsquo;t want to change any of the configuration settings, you can then skip ahead to triggering the sync on Obsidian, but let&amp;rsquo;s run through the options anyway.&lt;/p&gt;
&lt;h2 id="modifying-configuration-settings"&gt;Modifying configuration settings&lt;/h2&gt;
&lt;p&gt;On the Obsidian Exports page, you&amp;rsquo;ll be presented with a few options.&lt;/p&gt;
&lt;h3 id="group-files-in-category-folders"&gt;Group Files in Category Folders&lt;/h3&gt;
&lt;p&gt;By default, this will be enabled. When enabled, Readwise will put your highlights in different folders depending on its source. For example, Kindle highlights will appear in the folder &lt;code&gt;Books&lt;/code&gt;. Disabling this option means all new notes will be created in the directory specified in the Obsidian plugin settings, which we&amp;rsquo;ll cover later. I left this enabled in case I read a book that has the same name as a blog post about the book, for example.&lt;/p&gt;
&lt;h3 id="use-custom-formatting"&gt;Use Custom Formatting&lt;/h3&gt;
&lt;p&gt;Custom formatting is disabled by default, and lets you choose how the final note looks. Here are the things you can change in this section&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Page title&lt;/li&gt;
&lt;li&gt;Page metadata&lt;/li&gt;
&lt;li&gt;Highlights header&lt;/li&gt;
&lt;li&gt;Highlight&lt;/li&gt;
&lt;li&gt;YAML front matter&lt;/li&gt;
&lt;li&gt;Sync notification&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I did make some changes in this section just to tweak how I want information to be displayed.&lt;/p&gt;
&lt;h4 id="page-metadata"&gt;Page metadata&lt;/h4&gt;
&lt;p&gt;I don&amp;rsquo;t see the need for Page metadata since this is what I use YAML front matter for, so I removed the matadata section and the document tags (I never use those either). So my Page metadata section looks like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{% if image_url -%}
![rw-book-cover]({{image_url}})
{% endif -%}
{% if url -%}
URL: {{url}}
{% endif -%}
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="highlight"&gt;Highlight&lt;/h4&gt;
&lt;p&gt;By default, highlights are entered in bullet-point form. That&amp;rsquo;s fine when &lt;em&gt;I&amp;rsquo;m&lt;/em&gt; taking notes, but when I&amp;rsquo;m highlighting someone else&amp;rsquo;s words, I like to be very careful to use blockquotes. I frequently do block searches in Obsidian, and I do NOT want to accidentally pass off a quote as something I said.&lt;/p&gt;
&lt;p&gt;To this end, I added a &lt;code&gt;&amp;gt;&lt;/code&gt; before each highlight and two newlines (carriage returns) at the end to delineate each highlight. That way, I can quote just one highlight at a time.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; {{ highlight_text }}{% if highlight_location and highlight_location_url %} ([{{highlight_location}}]({{highlight_location_url}})){% elif highlight_location %} ({{highlight_location}}){% endif %}{% if highlight_note %}
- Note: {{ highlight_note }}{% endif %}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see above, I also removed highlight tags, which I never use.&lt;/p&gt;
&lt;h4 id="yaml-front-matter"&gt;YAML front matter&lt;/h4&gt;
&lt;p&gt;Metadata has to be quite specific, so I added parameters I already use for other notes in this section. The &lt;code&gt;TVZ&lt;/code&gt; is a tag I use that means &amp;ldquo;To Verzettelen&amp;rdquo;, meaning pages that I need to process further.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Title: {{full_title}}
Author: {{author}}
Tags: TVZ, readwise, {{category}}
date: {{date}}
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="sync-notification"&gt;Sync notification&lt;/h4&gt;
&lt;p&gt;As someone who has worked on a syncing plugin for Readwise to Obsidian, I know that things go wrong sometimes. Enabling this outputs a synchronization log to a note, which has come in handy MANY times in the past. &lt;em&gt;Has that book been synced, or did something go wrong?&lt;/em&gt; So I copied what the Readwise team suggested:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;- [[{{date|date(&amp;#39;Y-m-d&amp;#39;)}}]] {{time}} — Synced {{num_highlights}} highlight{{num_highlights|pluralize}} from {{num_books}} document{{num_books|pluralize}}.
{% for book in books %} - {{ book.num_highlights_added}} highlights from {{ book.title }}
{%endfor %}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="select-items-to-be-exported"&gt;Select Items to be Exported&lt;/h3&gt;
&lt;p&gt;In this section, you can choose which books/podcasts/other content in Readwise you want to export. It is disabled by default. If you haven&amp;rsquo;t synced Readwise to Obsidian before, and you just want to pull in everything, leave it disabled.&lt;/p&gt;
&lt;p&gt;In my case, I have already synchronized everything using the plugin I mentioned, and didn&amp;rsquo;t want to create another few hundred duplicate notes in my vault. So I enabled it and selected a few more recent books.&lt;/p&gt;
&lt;p&gt;A weird thing about this Export settings page is that there is no saving&amp;ndash; it just automatically saves it. Just a note, because I was scrolling up and down that page looking for an indication that it had been saved before I realized it is done automatically.&lt;/p&gt;
&lt;h2 id="initiate-sync-from-obsidian"&gt;Initiate Sync from Obsidian&lt;/h2&gt;
&lt;p&gt;Head back to Obsidian and to the Readwise Official plugin options page. You should see a &amp;ldquo;Initiate Sync&amp;rdquo; button where the &amp;ldquo;Connect&amp;rdquo; one was before.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/readwise-official-initiate.png" alt="Screenshot of Initiate Sync button" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a good sign - that means your Readwise account has been connected, and your configuration settings from the previous step will be fetched.&lt;/p&gt;
&lt;p&gt;Before you click the button, note that the &lt;em&gt;Customize base folder&lt;/em&gt; option is set to &lt;code&gt;Readwise&lt;/code&gt;. This is the directory that all your Readwise notes will be created. So if you enabled &lt;em&gt;Group Files in Category Folders&lt;/em&gt; earlier, your Kindle highlights will be saved in &lt;code&gt;Readwise/Books/booktitle.md&lt;/code&gt;. You can change the base folder if you prefer, but I just kept the default.&lt;/p&gt;
&lt;p&gt;Time to click the button!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/readwise-synced.png" alt="Screenshot of synced status from Readwise plugin" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see a few messages popping up on the upper right corner of your screen, and you&amp;rsquo;ll also see a &lt;em&gt;Synced!&lt;/em&gt; message appear next to the button you just clicked.&lt;/p&gt;
&lt;h2 id="verifying-results"&gt;Verifying results&lt;/h2&gt;
&lt;p&gt;In your Obsidian vault, search for one of the items you synced. Here&amp;rsquo;s what my note for Pat Flynn&amp;rsquo;s &lt;em&gt;Superfans&lt;/em&gt; looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/readwise-obsidian-kindle.png" alt="An example of Kindle highlights imported to Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Great! It looks exactly how I want it to look.&lt;/p&gt;
&lt;h2 id="set-up-automatic-sync"&gt;Set up automatic sync&lt;/h2&gt;
&lt;p&gt;If you leave it there, the plugin will only sync when you tell it to, so you&amp;rsquo;d need to go into the plugin options each time. That&amp;rsquo;s not particularly useful for me, because I want it to just happen.&lt;/p&gt;
&lt;p&gt;If you feel similarly, head back into Plugin Options one more time and select your desired frequency in the &lt;em&gt;Configure resync frequency&lt;/em&gt; dropdown menu. I chose 1 hour, the minimum. I also enabled the automatic sync when Obsidian opens.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/readwise-official-auto-sync.png" alt="Screenshot of auto sync options for Readwise Official plugin" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I love this new plugin! It was very much needed, because existing options for accomplishing the same thing were flawed in some way, mainly because they were community-driven&amp;ndash; meaning the people who created these workarounds (myself included) were &amp;ldquo;just&amp;rdquo; users who had other jobs. I&amp;rsquo;m grateful that the Readwise team has acknowledged the popularity of Obsidian and taken on the maintenance burden. I&amp;rsquo;ve experimented with the plugin quite a bit and haven&amp;rsquo;t found any major issues so far. It is a seamless way to get the best of the content I&amp;rsquo;ve consumed from various sources into one single source of truth: Obsidian. I highly recommend it to anyone looking to streamline content processing and creation processes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This post contains an affiliate link for &lt;a href="https://readwise.io/nicole"&gt;Readwise&lt;/a&gt;. Clicking on that link will give you a two-month trial of Readwise, double the usual amount, and they give me a small percentage if you sign up for a plan. Check out &lt;a href="https://nicolevanderhoeven.com/ethics/"&gt;My Ethics Statement&lt;/a&gt; to see my standards for promotions.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>How I use Obsidian for D&amp;D - Player's Edition</title><link>https://nicolevanderhoeven.com/blog/20210809-dnd-obsidian-player/</link><pubDate>Mon, 09 Aug 2021 17:46:51 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210809-dnd-obsidian-player/</guid><description>
&lt;meta property=“og:image” content="/assets/Forgotten-Realms-workspace-in-Obsidian.png" /&gt;
&lt;meta property=”og:image:width” content=”1200″ /&gt;
&lt;meta property=”og:image:height” content=”628″ /&gt;
&lt;p&gt;I&amp;rsquo;ve previously written about &lt;a href="https://nicolevanderhoeven.com/blog/20210518-how-i-use-obsidian-at-work/"&gt;how I use Obsidian for work&lt;/a&gt;, and I&amp;rsquo;ve also talked about &lt;a href="https://nicolevanderhoeven.com/blog/20201214-roam-for-dnd/"&gt;how I use Roam Research for D&amp;amp;D&lt;/a&gt;. Shortly after the latter post, however, I moved to Obsidian. That was six months ago, so I thought it was time for an update.&lt;/p&gt;
&lt;h2 id="before-the-game"&gt;Before the game&lt;/h2&gt;
&lt;h3 id="world"&gt;World&lt;/h3&gt;
&lt;p&gt;My World page is the main jumping-off point for all my notes for a specific campaign, and each campaign gets one. It contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The main locations in the world&lt;/li&gt;
&lt;li&gt;The player characters, and links to their individual pages with information about what my character knows or thinks of them&lt;/li&gt;
&lt;li&gt;A link to Open Loops (Quests and Questions and Agenda, as discussed below)&lt;/li&gt;
&lt;li&gt;The list of all the sessions I&amp;rsquo;ve played for the campaign&lt;/li&gt;
&lt;li&gt;Campaign-specific house rules that the DM has introduced&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/world-page.png" alt="The World page for my Forgotten Realms campaign" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="quests-and-questions"&gt;Quests and Questions&lt;/h3&gt;
&lt;p&gt;I like to play high-intelligence characters, so I like to keep track of unanswered questions and other adventure hooks my DM has dropped that the party hasn&amp;rsquo;t quite gotten around to resolving yet. Some of them are critical quest or story arcs, and others are random tidbits of information that NPCs have mentioned. I organize them by location, and then by quest, so that it&amp;rsquo;s easy to follow a thread depending on where the party is or how much downtime we have. Here&amp;rsquo;s an example of that page:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/quests-and-questions.png" alt="Quests and Questions page" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="agenda"&gt;Agenda&lt;/h3&gt;
&lt;p&gt;While the Quests and Questions page records unfinished business that my character is actively thinking about but may not quite know how to explore yet, my Agenda page is where I write down specific tasks. I loosely follow the &lt;a href="https://gettingthingsdone.com/"&gt;Getting Things Done&lt;/a&gt; methodology and divide this page into Next Actions and Waiting For. I group tasks by person and/or by location.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/Agenda-sample.png" alt="Agenda page" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I use the Obsidian &lt;a href="https://github.com/DahaWong/obsidian-completed-area"&gt;Completed Area plugin&lt;/a&gt; so that hitting a CTRL + ENTER on a task automatically checks it and then moves that task to a separate Completed section at the bottom of the note. This helps me keep the agenda clean and relevant.&lt;/p&gt;
&lt;h2 id="during-a-game"&gt;During a game&lt;/h2&gt;
&lt;h3 id="workspace"&gt;Workspace&lt;/h3&gt;
&lt;p&gt;One of the best parts about Obsidian is that it allows me to save and load custom workspaces. This is useful because I use Obsidian for work as well as play, so I can set up the two workspaces differently. I also DM one campaign and play in another, and I have a workspace for each of them because I need different information available to me. The workspace features lets me customize my dashboard. Here&amp;rsquo;s what my workspace looks like for the 4e game that I play in:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/Forgotten-Realms-workspace-in-Obsidian.png" alt="My Forgotten Realms campaign workspace in Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;At the beginning of a game, I&amp;rsquo;ll create a new session page in the last pane, enter into it, and then stay in the workspace for the rest of the game. I have my character&amp;rsquo;s stats sheet handy (which I need to do manually since I haven&amp;rsquo;t found a D&amp;amp;D Beyond-like service for 4e) and all the powers and abilities I need to refer to.&lt;/p&gt;
&lt;h3 id="session-template"&gt;Session template&lt;/h3&gt;
&lt;p&gt;I love Obsidian templates! I use templates for things like inserting an HP tracker with all my party&amp;rsquo;s names, their max HP, healing surges (4e); a defense table to keep track of a creature&amp;rsquo;s defenses (again, 4e); and campaign-specific session log formats.&lt;/p&gt;
&lt;p&gt;At the beginning of the game, I&amp;rsquo;ll create a new page in the Session List, click into it, and insert a session log template (I have this hotkeyed to CMD+&lt;code&gt;;&lt;/code&gt;). Here&amp;rsquo;s what a fresh one looks like, in Edit mode:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/session-template-fresh.png" alt="My session template, newly inserted" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I add metadata to every session so that I can query it later using the excellent &lt;a href="https://github.com/blacksmithgu/obsidian-dataview"&gt;Obsidian Dataview plugin&lt;/a&gt;, though I do this more often for the campaign I DM rather than the one I play in.&lt;/p&gt;
&lt;p&gt;We usually do a recap at the beginning of the game, so I jump into the &lt;code&gt;Recap of the last session&lt;/code&gt; section and embed the summary from the previous session using &lt;code&gt; ![](/assets/86_20210717 Day 16 Hunting the Ice Phase Spider#^summary)&lt;/code&gt;, which pulls in the section marked with &lt;code&gt;^summary&lt;/code&gt; from the last session. That embeds just that portion of the last session in the current session, like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/Recap.png" alt="Session recap using block embed" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This helps me get back up to speed quickly instead of doing the usual &amp;ldquo;Where were we again?&amp;rdquo; that takes up time.&lt;/p&gt;
&lt;h3 id="hp-table"&gt;HP table&lt;/h3&gt;
&lt;p&gt;My character is a multiclassed healer, so it&amp;rsquo;s important for me to know who needs heals or other buffs. I keep track of this separately, and it helps out my DM as well because it&amp;rsquo;s easy to miss something in the excitement of a battle. I do include the HP table in my workspace, in Preview mode, but here&amp;rsquo;s what it looks like in Edit mode:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/HP-table.png" alt="A table of the party’s HP, surges, and conditions" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Tables in Markdown can be a pain, but the &lt;a href="https://github.com/tgrosinger/advanced-tables-obsidian"&gt;Advanced Tables plugin&lt;/a&gt; goes a long way towards making this easier by adding some nice features like tabbing to the next cell, adding commands for inserting and deleting a column or row, and just automatically formatting a table so that it&amp;rsquo;s not a jumble of lines. I also heavily use this plugin for my characters stats and skills, both of which I keep in tables.&lt;/p&gt;
&lt;h3 id="keeping-track-of-fights"&gt;Keeping track of fights&lt;/h3&gt;
&lt;p&gt;When a fight breaks out, I insert an intiative template, which is just a list of all the characters in the party:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Initiative!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Ka&amp;#39;ira
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Maestro
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Xio
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Eraithe
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Keren
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This makes it easier to add the initiative value rolled, as well as that of any other baddies we&amp;rsquo;re fighting. I use the &lt;a href="https://github.com/vslinko/obsidian-outliner"&gt;Outliner plugin&lt;/a&gt; to be able to easily move people on the list up or down as needed without having to copy or paste. I have the shortcuts set to CMD+SHIFT+UP and CMD+SHIFT+DOWN so I don&amp;rsquo;t even need to use my mouse.&lt;/p&gt;
&lt;p&gt;I also have a template for Defenses. D&amp;amp;D 4th Edition has four different kinds of defenses, and since my character&amp;rsquo;s powers target a few of them, I keep track of the defenses of each monster. Here&amp;rsquo;s what that template looks like, in Edit mode:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| Defense | Value |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| --------------------- | ----- |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| [[Reflex Defense]] | |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| [[Fortitude Defense]] | |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| [[Will Defense]] | |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| [[Armor Class]] | |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll notice that I also link to the pages I have for each defense type, in case I need to remind myself which stat a defense is comprised of.&lt;/p&gt;
&lt;h3 id="character-powers"&gt;Character powers&lt;/h3&gt;
&lt;p&gt;Like many, I have migrated from playing games on a literal tabletop to doing it virtually. These days, I play 100% online, using a bunch of different tools. For the most part, though, I still prefer to roll actual dice. There&amp;rsquo;s just something satisfying about the feel of a die in my hand. Since I switched to Obsidian, though, I have looked into virtual dice. On the off chance I don&amp;rsquo;t have any dice (ha) or all my dice have betrayed me (much more likely), I use the &lt;a href="https://github.com/valentine195/obsidian-dice-roller"&gt;Dice Roller plugin&lt;/a&gt;. It turns&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;`dice: 1d20+7`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;into a random die roll. Here it is when incorporated into my character&amp;rsquo;s list of powers:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/powers-sample.png" alt="My character’s powers page" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Sometimes, when I&amp;rsquo;m feeling superstitious about my dice, I&amp;rsquo;ll switch to using this dice roller &amp;ldquo;just to be safe&amp;rdquo;. It has other cool features too, which I&amp;rsquo;ll cover more in detail in the Dungeon Master&amp;rsquo;s edition of this article.&lt;/p&gt;
&lt;h3 id="search-linked-unlinked-mentions"&gt;Search, linked, unlinked mentions&lt;/h3&gt;
&lt;p&gt;Frequently, something comes up in a game that I&amp;rsquo;m &lt;em&gt;sure&lt;/em&gt; we&amp;rsquo;ve heard of before, but I can&amp;rsquo;t quite remember where or when. Actually, sometimes something comes up that I&amp;rsquo;m quite sure we&amp;rsquo;ve &lt;em&gt;never&lt;/em&gt; heard of before, but it turns out we have. Luckily, Obsidian is quick to tell me that using the linked/unlinked mentions and the search features.&lt;/p&gt;
&lt;p&gt;Here are the mentions in my note for the word &amp;ldquo;Netheril&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/New%20Folder%20With%20Items/Netheril-mentions.png" alt="Linked mentioned for the word Netheril" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Unlinked mentions show up too, in case the word came up somewhere and I didn&amp;rsquo;t think to specifically create a page on it.&lt;/p&gt;
&lt;p&gt;On monster pages, mentions remind me when we fought them last, and any tips for fighting them. It can be handy when one of the other players has forgotten something that happened to them, too. If I want to remember how Ka&amp;rsquo;ira got her festering wound, I can type it into the Obsidian search bar:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/festering-wound.png" alt="A search in Obsidian for “festering wound”" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;and be able to confidently say that Ka&amp;rsquo;ira got her last festering wound on session 68, in the Blue Crystal Fortress that we now know to be the Twisted Fane, while fighting aberrations.&lt;/p&gt;
&lt;h2 id="after-a-game"&gt;After a game&lt;/h2&gt;
&lt;p&gt;I think I might use Obsidian even more between games than during the game, because after the game is when I process everything my character learned.&lt;/p&gt;
&lt;h3 id="session-summary"&gt;Session summary&lt;/h3&gt;
&lt;p&gt;Immediately after the game is when I create a short session summary. I like to do this when the session is still fresh in my mind. Then, I add &lt;code&gt;^summary&lt;/code&gt; at the end of the paragraph to identify it and to be able to pull it into the next session&amp;rsquo;s recap. Block references and block embeds are some of my most frequently used Obsidian features.&lt;/p&gt;
&lt;h3 id="creating-new-pages"&gt;Creating new pages&lt;/h3&gt;
&lt;p&gt;I then create new pages for any people, places, magical items, or events that we encountered. I like to use a standard frontmatter:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;---
type:
location: [Neverwinter]
world: &amp;#34;Forgotten Realms&amp;#34;
campaign: &amp;#34;Order of the Wandering Mind&amp;#34;
date: 2021-06-23
---
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s an example of the page for an NPC:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/npc-sample.png" alt="An NPC page in my campaign" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;If my DM has given us character art, I include it in the page.&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s an example of a page for a location:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/twisted-fane-location-example.png" alt="Location page: Twisted Fane" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;For this page, I used a plugin called &lt;a href="https://github.com/valentine195/obsidian-leaflet-plugin"&gt;Obsidian Leaflet&lt;/a&gt;, which lets me add maps to my notes! In this case, my DM gave us this map of the structure as a guide, but during combats we didn&amp;rsquo;t use a map. To keep myself oriented between games, I added red pawprint pins where we had spotted or fought monsters, green thumbtacks to mark where we&amp;rsquo;d ended sessions, and green markers where we had spotted allies. I also like to add links to monsters or sessions on the pins/markers so that I can jump to those pages quickly if I need to.&lt;/p&gt;
&lt;h3 id="history"&gt;History&lt;/h3&gt;
&lt;p&gt;To keep historical events straight in my head, especially since we tend to discover them out of sequence, I use the &lt;a href="https://github.com/Darakah/obsidian-timelines"&gt;Obsidian Timelines plugin&lt;/a&gt;, which lets me create pages like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-timeline-for-dnd.png" alt="Obsidian Timeline in my game" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;by using this code in the timeline page:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;``&lt;span style="color:#e6db74"&gt;`timeline
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;fr
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;\`&lt;/span&gt;``
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(without the backtick) and this for every event that I want to include:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;date&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;2021-06-23&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;tags&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;timeline, fr]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;span
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; class=&amp;#39;ob-timelines&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data-date=&amp;#39;1443-00&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data-title=&amp;#39;Xionerys is born&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data-class=&amp;#39;orange&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data-img = &amp;#39;assets/xionerys.jpg&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data-type=&amp;#39;range&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data-end=&amp;#39;1443-00&amp;#39;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Xionerys is born in Calimshan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/span&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using this method, I&amp;rsquo;ve been able to call out discrepancies in what two different NPCs told us and figure out that one was lying&amp;ndash; or at least misinformed.&lt;/p&gt;
&lt;h3 id="replace-all"&gt;Replace all&lt;/h3&gt;
&lt;p&gt;When I inevitably get a fantasy name wrong, having pages as flat .md files is great because I can do a find and replace on ALL mentions of a certain NPC name/place I&amp;rsquo;ve misspelled (those fantasy names!). I do this with my usual code/text editor, VSCode, just because I usually already have it up, but you can do it using terminal commands. If it&amp;rsquo;s just the name of a page, updating the spelling in Obsidian changes it everywhere else it&amp;rsquo;s linked to as well, which is useful.&lt;/p&gt;
&lt;h3 id="updating-open-loops"&gt;Updating open loops&lt;/h3&gt;
&lt;p&gt;To finish processing the last session, I update Open Loops: I add new points or update existing points in Quests and Questions, and then add tasks to Agenda that I want to do in the next session. I find that it&amp;rsquo;s best to do it after a game because it&amp;rsquo;s a week, sometimes more, between games and there&amp;rsquo;s a high likelihood I&amp;rsquo;ll have forgotten what I want to do by then!&lt;/p&gt;
&lt;h3 id="publish"&gt;Publish&lt;/h3&gt;
&lt;p&gt;I also publish my D&amp;amp;D notes, just for transparency. My DM has joked that he looks in there sometimes when he&amp;rsquo;s forgotten something. Even if he doesn&amp;rsquo;t, I enjoy having my notes so easily accessible. I often navigate there myself when I have a spare moment, remembering old sessions and thinking about some loose threads. If you&amp;rsquo;re interested, &lt;a href="https://notes.nicolevanderhoeven.com/TTRPGs/Order+of+the+Wandering+Mind/World"&gt;here they are&lt;/a&gt;! Just be warned that the plugins won&amp;rsquo;t work in the web version, so sometimes you&amp;rsquo;ll come across bits of code that didn&amp;rsquo;t render. I also migrated these notes from Roam Research, and haven&amp;rsquo;t gone back and updated all the older notes.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Since I&amp;rsquo;ve switched to Obsidian, it&amp;rsquo;s been an indispensable part of my workflow for D&amp;amp;D. Without it, I&amp;rsquo;d be just another murder hobo with no notes. And no DM wants that!&lt;/p&gt;</description></item><item><title>Gear I Use</title><link>https://nicolevanderhoeven.com/gear/</link><pubDate>Mon, 12 Jul 2021 21:18:55 +0200</pubDate><guid>https://nicolevanderhoeven.com/gear/</guid><description>&lt;h1 id="gear-i-use"&gt;Gear I Use&lt;/h1&gt;
&lt;p&gt;One of my favorite obsessions is watching or reading about the gear other people use. I&amp;rsquo;ve watched almost every &amp;ldquo;What&amp;rsquo;s in my tech bag&amp;rdquo; video on YouTube, it feels like, and I&amp;rsquo;m always prowling blog posts that detail what someone is using. In case there&amp;rsquo;s anybody out there that is as interested in these things as I am, here&amp;rsquo;s an overview of the gear that I use to make videos, write both blog posts and code, and generally mess around on the internet.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Edit: After a reader suggested I provide links to these items, I&amp;rsquo;ve included them below, and some are affiliate links. You are under no obligation to click these links, but if you do, I get a tiny percentage of the sale. Thanks!&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="sections"&gt;Sections&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a lot here! I&amp;rsquo;ve broken it down into sections, so you can skip to the section you&amp;rsquo;re interested in.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#daily-hardware"&gt;Daily hardware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#home-office-1-the-netherlands"&gt;Home Office 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#home-office-2-portugal"&gt;Home Office 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#my-everyday-carry"&gt;Everyday Carry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#my-remote-working-tech-bag"&gt;Remote Working Tech Bag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#my-remote-work-setup-for-a-month"&gt;Remote Work Setup for a month&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="daily-hardware"&gt;Daily hardware&lt;/h2&gt;
&lt;p&gt;(&lt;a href="#sections"&gt;back to top&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Here are the main devices I use every day.&lt;/p&gt;
&lt;h3 id="computers"&gt;Computers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3uOnkJ0"&gt;Macbook Air 15&amp;quot; (2023)&lt;/a&gt;: 24 GB RAM, 2TB SSD, M2 chip - this is my main workhorse&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZggnG"&gt;Macbook Pro 13&amp;quot; (2020)&lt;/a&gt;: 2,3 GHz Quad-Core Intel Core i7, 32 GB RAM - secondary laptop&lt;/li&gt;
&lt;li&gt;Mac Mini (&lt;a href="https://amzn.to/3t3B968"&gt;later version here&lt;/a&gt;): M1 chip, 16GB RAM, 1 TB SSD - desktop, always on&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mobiles"&gt;Mobiles&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/47FaefX"&gt;Samsung Galaxy Z Fold 5&lt;/a&gt;: 512GB - my main mobile, always with me&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apple.com/iphone-15-pro/"&gt;Apple iPhone 15&lt;/a&gt;: Pro Max - secondary mobile, always with me&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CfKxmH"&gt;Samsung Galaxy Note 10+&lt;/a&gt;: extra mobile&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tablets"&gt;Tablets&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CgzR7E"&gt;Apple iPad Pro 11&amp;quot;&lt;/a&gt; (2020) tablet - still works great!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="readers"&gt;Readers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3GrSbOc"&gt;Amazon Kindle Scribe&lt;/a&gt;: my couch reader, for nonfiction and PDFs&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3xsRwFy"&gt;Amazon Kindle Oasis&lt;/a&gt;: my bed reader, for fiction&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/46LEZ1k"&gt;Kobo Sage&lt;/a&gt;: for non-Amazon ebooks&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="wearables"&gt;Wearables&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ouraring.com/product/heritage-silver/step1"&gt;Oura&lt;/a&gt;: health and sleep tracking ring&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/417Szev"&gt;Samsung Galaxy Watch 6&lt;/a&gt;: smartwatch&lt;/li&gt;
&lt;li&gt;American Express Prada wearable (not available for purchase separately): for contactless payments&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="audio"&gt;Audio&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/47RdhBo"&gt;Apple AirPods Pro&lt;/a&gt;: second generation, for Apple products&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3p6OHLJ"&gt;Bose QuietComfort Earbuds II&lt;/a&gt;: for non-Apple products&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="home-office-1-the-netherlands"&gt;Home Office 1 (the Netherlands)&lt;/h2&gt;
&lt;p&gt;(&lt;a href="#sections"&gt;back to top&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/gear-study.JPG" alt="A photo of my home office on any given day" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="video-production-gear"&gt;Video production gear&lt;/h3&gt;
&lt;h4 id="not-on-desk"&gt;Not on desk&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jcSNva"&gt;Elgato green screen&lt;/a&gt; (long, black metal object on the left side of the photo, on the floor)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ltqOdy"&gt;Canon 6D Mark II&lt;/a&gt; with a &lt;a href="https://amzn.to/3rTm6XK"&gt;Canon 16-35mm f/2.8L&lt;/a&gt; (upper left, on tripod with teleprompter)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rQ1kIl"&gt;Manfrotto 290 tripod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2TQA3sD"&gt;TeleprompterPAD iLight Pro 10&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jiNpH1"&gt;Neewer 5-in-1 reflective reflector set&lt;/a&gt; (gold, silver, white, black, and transparent)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fpnkVC"&gt;Neewer two-way rotating aluminum light stand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jen4K2"&gt;Manfrotto universal smartphone clamp&lt;/a&gt; (mounted on light stand above desk)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2WSA3tt"&gt;Neewer Camera Monopod&lt;/a&gt; with rotating column for panoramic shooting&lt;/li&gt;
&lt;li&gt;2 x &lt;a href="http://en.nanlite.com/products/detail-174"&gt;Nanlite PavoTube 6C II&lt;/a&gt; tubes on the shelves in the background&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="on-desk"&gt;On desk&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fub99P"&gt;Aputure AL-MC&lt;/a&gt; portable light (attached magnetically to the back of my external monitor)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3foIliT"&gt;Blue Yeti&lt;/a&gt; microphone - (&lt;em&gt;pictured, but retired for the next item&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3EryDHu"&gt;Røde PodMic&lt;/a&gt; microphone - &lt;em&gt;my new main mic&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3s46hxv"&gt;Elgato Key Light&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3yi8VSp"&gt;Canon M50&lt;/a&gt; (between my laptop and monitor screens, used as my main webcam)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3A7HKdv"&gt;Elgato multi-mount&lt;/a&gt; (holding up the M50)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fv26FA"&gt;Blackmagic Atem Mini&lt;/a&gt; video switcher&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZfVRW"&gt;Røde Wireless Go&lt;/a&gt; wireless microphone (secondary microphone when I need to move around)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3m3JeSa"&gt;Rødecaster Pro&lt;/a&gt; for crispy audio on calls and videos&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2Vcj84H"&gt;Samsung Portable SSD T5, 1TB&lt;/a&gt; (always connected to my laptop) the drive I edit off&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZggnG"&gt;Macbook Pro 13&amp;quot; (2020)&lt;/a&gt;, 2,3 GHz Quad-Core Intel Core i7, 32 GB RAM - main laptop&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="not-pictured-but-in-frequent-use"&gt;Not pictured but in frequent use&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CmuLH1"&gt;Sony A6600&lt;/a&gt; with &lt;a href="https://amzn.to/3xn3ZKD"&gt;Sigma 16mm f1.4&lt;/a&gt; lens (main shooter, used to take this shot)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2Vc1A8R"&gt;Røde Wireless Go II&lt;/a&gt; two-person microphones (used as on-board camera mics)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fowSA0"&gt;SwitchPod tripod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.peakdesign.com/pages/travel-tripod"&gt;Peak Design Travel tripod&lt;/a&gt;: Pricy, but the only full tripod I use these days. Excellent for travel!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="other-tech"&gt;Other tech&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3AbSrff"&gt;Synology DS920+&lt;/a&gt; 4 Bay NAS with 4 x &lt;a href="https://amzn.to/2VesKfg"&gt;WD RED 4TB&lt;/a&gt; drives (just outside the photo)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3iki732"&gt;Amazon Echo Dot&lt;/a&gt; (on the bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZh0Ju"&gt;Google Wifi&lt;/a&gt; wireless extender (just outside photo)&lt;/li&gt;
&lt;li&gt;2 x &lt;a href="https://amzn.to/3jcWy3K"&gt;Nintendo Switch&lt;/a&gt; (on bookshelf; one &lt;a href="https://amzn.to/3jgXK6h"&gt;Animal Crossing edition&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3A8PBr8"&gt;Nintendo Lite&lt;/a&gt; Zacien and Zamazenta edition (on bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3xsRwFy"&gt;Amazon Kindle Oasis&lt;/a&gt; (bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/37fVvur"&gt;Amazon Kindle Paperwhite&lt;/a&gt; (bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://us.kobobooks.com/products/kobo-aura-one/"&gt;Kobo Aura One&lt;/a&gt; (bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thomannmusic.com/risa_uke_solid_tenor.htm"&gt;Risa Electric Solid Tenor Ukulele&lt;/a&gt;, black (bookshelf)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="non-tech-gear"&gt;Non-tech gear&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wyrmwoodgaming.com/products/cherry-magnetic-dice-tower/"&gt;Wyrmwood Gaming Cherry magnetic dice tower bundle&lt;/a&gt; including dice tower, personal dice tray, and dice vault&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jioG5p"&gt;Kimi Rainbow Acrylic kalimba&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CeHdIw"&gt;Visconti Homo Sapiens Lava&lt;/a&gt; Midi, steel age fountain pen (the only fountain pen of many that I use regularly)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/desksetup.jpg" alt="A photo of my the things on my desk" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="non-video-gear-on-my-desk"&gt;Non-video gear on my desk&lt;/h3&gt;
&lt;p&gt;I already listed the video-related gear on my desk in the first section above, but here&amp;rsquo;s everything else.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fst7JV"&gt;Samsung Galaxy S10&lt;/a&gt; (secondary phone, primarily for timelapses and overhead videos)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3lrYgkl"&gt;Samsung Galaxy Wireless Buds&lt;/a&gt;, rose&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CfKxmH"&gt;Samsung Galaxy Note 10+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3lotmtj"&gt;Apple Macbook Air 11&amp;quot;&lt;/a&gt; (2011, used as my presentation laptop)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3inWn6g"&gt;Apple Magic Trackpad&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CgzR7E"&gt;Apple iPad Pro 11&amp;quot;&lt;/a&gt; (2020) tablet&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nizkeyboard.com/collections/peripherals/products/niz-2019-new-atom-68-ec-bluetooth-keyboard-rgb-or-non-rgb?variant=30346364059719"&gt;Niz Plum Atom 68&lt;/a&gt; electro-capacitive keyboard with &lt;a href="https://www.etsy.com/listing/902250753"&gt;Pikachu keycap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3xpYeMe"&gt;Logitech MX Master 3&lt;/a&gt; the best mouse I&amp;rsquo;ve used!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3lt8rFi"&gt;Bose NC700&lt;/a&gt; wireless noise-cancelling headphones&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ouraring.com/product/heritage-silver/step1"&gt;Oura&lt;/a&gt; health and sleep tracking ring&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="home-office-2-portugal"&gt;Home Office 2 (Portugal)&lt;/h2&gt;
&lt;p&gt;(&lt;a href="#sections"&gt;back to top&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;I live between two countries: the Netherlands and Portugal. Since I spend a few months in each place, that also means I need a place to work in each apartment! My home office in Portugal is larger but definitely less well-equipped. It&amp;rsquo;s a little embarrassing, but I thought it would be interesting to show a more realistic, in-progress setup.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/portugal-home-office.jpeg" alt="A photo of my home office in Portugal" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Why yes, I do indeed have clothes hanging on a rack behind my desk to try and soak up sound.&lt;/p&gt;
&lt;h3 id="desk"&gt;Desk&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Elgato Key Light Air x 2: I bought two of the smaller key lights than what I have in the Netherlands, because this room is larger and requires more light&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3M9LXot"&gt;Hidrate Spark Pro&lt;/a&gt;: bluetooth water bottle&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SK42vf"&gt;Samsung 32&amp;quot; 4K UHD monitor&lt;/a&gt;: a bit overkill on the size front, but nice&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fxf9cs"&gt;Apple iPad Pro 11&amp;quot;&lt;/a&gt;: This is from 2020, but still going strong&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3dRY3pi"&gt;Elgato Stream Deck MK.2&lt;/a&gt;: 15 programmable keys for shortcuts and macros&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rp6fAo"&gt;Keychron K6&lt;/a&gt;: hotswappable, RGB mechanical keyboard&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3UQdkaU"&gt;Logitech MX Anywhere 3&lt;/a&gt;: my travel mouse&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SHehAv"&gt;Blackmagic Atem Mini&lt;/a&gt;: my video switcher&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3E7gSzA"&gt;Røde Rodecaster Pro II&lt;/a&gt;: my audio mixer&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3Sm6LLw"&gt;Røde PSA1&lt;/a&gt;: the best mic arm&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ftGRqD"&gt;Røde Podmic&lt;/a&gt;: great XLR microphone&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SoyTxK"&gt;Samsung Galaxy Note 10+&lt;/a&gt;: my secondary phone&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3LU6g8Z"&gt;Baseus 17-in-1 USB-C Docking Station&lt;/a&gt;: for connecting my laptop to all the peripherals&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CmGNCc"&gt;Nintendo Switch Lite&lt;/a&gt; in coral: my travel gaming console&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fully.com/accessories/jax-dual-monitor-arm.html"&gt;Fully monitor and laptop arm&lt;/a&gt;: arms to hold my monitor and laptop&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZggnG"&gt;Macbook Pro 13&amp;quot; (2020)&lt;/a&gt;: same laptop that I bring around with me&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CmuLH1"&gt;Sony A6600&lt;/a&gt; with &lt;a href="https://amzn.to/3xn3ZKD"&gt;Sigma 16mm f1.4&lt;/a&gt; lens (main camera that I take with me from the Netherlands)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CipwtH"&gt;SwitchPod DSLR Tripod&lt;/a&gt;: best travel tabletop tripod&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="free-standing"&gt;Free-standing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.fully.com/standing-desks/jarvis.html?msclkid=9636778336e7157a372314b5f00325e1"&gt;Fully Jarvis standing desk&lt;/a&gt;: bamboo-topped motorized desk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3BPFQR6"&gt;Neewer 77 inch tripod&lt;/a&gt;: basic tripod&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3C09bIC"&gt;Neewer Photo Studio Backdrop Support System&lt;/a&gt;: I hung an acoustic curtain and some clothes on this to help with echo for now&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="not-pictured"&gt;Not pictured&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fls6Gu"&gt;Samsung Galaxy Z Fold 3&lt;/a&gt;: used to take this photo&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="my-everyday-carry"&gt;My everyday carry&lt;/h2&gt;
&lt;p&gt;(&lt;a href="#sections"&gt;back to top&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;What about what I carry every day?&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/dddHAwrCpog?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3xX8y1A"&gt;Samsung Galaxy Active 2&lt;/a&gt;: my smartwatch&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.etsy.com/listing/1108272100/critsuccess-d20-dice-ring-in-black"&gt;d20 spinner ring&lt;/a&gt;: a ring that acts like a 20-sided die for roleplaying games on the go&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fls6Gu"&gt;Samsung Galaxy Z Fold 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aersf.com/day-sling-3-xpac-black"&gt;Aer Day Sling 3 X-Pac&lt;/a&gt;: my sling bag of choice&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fnkTp6"&gt;Nitecore Tini 2 USB C flashlight&lt;/a&gt;: tiny USB-C rechargeable flashlight&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SuWBru"&gt;Nite-Ize locking S-biners&lt;/a&gt;: for a little extra security for zippers&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SyDpJI"&gt;Orbitkey holder&lt;/a&gt;: to hold my keys&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nanobag.net/"&gt;Nano bag 2.0&lt;/a&gt;: compactable bag&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3r7zGXK"&gt;Kable Card&lt;/a&gt;: all essential cables and adapters, a light, a mobile stand, and a memory card reader all in one card&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SfNP11"&gt;Aurtech bluetooth keyboard&lt;/a&gt;: portable bluetooth keyboard&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3DWI6ZQ"&gt;Samsung Galaxy S Pen Fold Edition&lt;/a&gt;: stylus for my mobile&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3RhiFoL"&gt;Sony WF 1000 XM3&lt;/a&gt;: wireless noise-cancelling earbuds&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3RgZrj4"&gt;Spigen Arc Powerstation 20W&lt;/a&gt;: small GaN charger&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3LOwYA6"&gt;Secrid mini-wallet&lt;/a&gt;: my minimalist wallet&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dispeldice.com/collections/legacy/products/lavender-dreams-7-piece-polyhedral-set"&gt;Dispel Dice Lilac Song&lt;/a&gt;: a full-sized d20 I carry around&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buttonshygames.com/collections/current-collection/products/sprawlopolis"&gt;Sprawlopolis&lt;/a&gt;: small cooperative card game&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SibAW9"&gt;LIE&lt;/a&gt;: fun party game for 2-6 players and is the size of a pack of gum&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.victorinox.com/us/en/Products/Swiss-Army-Knives/Small-Pocket-Knives/Jetsetter/p/0.6263"&gt;Victorinox Jetsetter&lt;/a&gt;: small, travel-friendly multitool&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mightyhanks.com/index.php?route=product/category&amp;amp;path=29"&gt;Mighty Hanks&lt;/a&gt;: microfiber on one side, cotton on the other&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3DW1RRa"&gt;Burts Bees&lt;/a&gt;: lip balm&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3SDfc4H"&gt;Small red Sharpie&lt;/a&gt;: mini marker&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ClU7Xz"&gt;Joby Gorillapod GripTight Smartphone Micro Tripod&lt;/a&gt;: tripod for my mobile&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3dIL4pR"&gt;Samsung Galaxy Note 10+&lt;/a&gt;: secondary mobile&lt;/li&gt;
&lt;li&gt;&lt;a href="https://topodesigns.com/products/accessory-bags?variant=39887314157621"&gt;Topo designs micro pouch&lt;/a&gt;: small pouch&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3LOOeoD"&gt;Tile Mate Pro&lt;/a&gt;: bluetooth tracker&lt;/li&gt;
&lt;li&gt;&lt;a href="https://shop.trezor.io/product/trezor-model-t"&gt;Trezor Model T&lt;/a&gt;: hardware wallet and security key&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="my-remote-working-tech-bag"&gt;My Remote Working Tech Bag&lt;/h2&gt;
&lt;p&gt;(&lt;a href="#sections"&gt;back to top&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;If I need to go somewhere to work, my EDC bag won&amp;rsquo;t cut it! Here&amp;rsquo;s what I bring with me to work and play remotely:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/fBEJM-31cjs?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.aersf.com/special-projects"&gt;Aer x Ministry of Supply Lunar Pack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Platypus soft bottle. &lt;a href="https://amzn.to/3TWaEH1"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3gupCGD"&gt;PacSafe Retractasafe 250&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.nl/-/en/dp/B08C4WCJNP"&gt;iWalk 10,000 mAH USB C power bank&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://shop.critrole.com/products/nott-the-brave-adult-face-masks-two-pack"&gt;Critical Role Nott the Brave face mask (2 pack)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3TVWNAn"&gt;Apple iPad Pro 11&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3GD7V2m"&gt;Apple Pencil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3EmoRqQ"&gt;The Crew: Mission Deep Sea (card game)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3VmnijP"&gt;Kindle 2022&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3OtmVBx"&gt;Popsockets PopGrip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3EQy3FE"&gt;Nintendo Switch Lite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aersf.com/day-sling-3-xpac-black"&gt;Aer Day Sling 3 X-Pac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3VjNsU6"&gt;SwitchPod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.peakdesign.com/products/tech-pouch/"&gt;Peak Design Tech Pouch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.keychron.com/products/keychron-travel-pouch?variant=32264197701721"&gt;Keychron K3 travel pouch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3tMKFax"&gt;Nuphy Air 60 keyboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ElQfW8"&gt;Roost laptop stand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nedrelow.com/en-nl/products/magic-sleeve?variant=31842687189057"&gt;Nedrelow Magic Sleeve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.etsy.com/listing/691535271/soft-wool-felt-dice-tray-soft-tray"&gt;URWizards soft wool felt dice tray&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ErrRm1"&gt;Shure Aonic 215 wired sound isolating earbuds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3Ers4pj"&gt;Logitech MX Anywhere 3&lt;/a&gt;: travel mouse&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3GxVHId"&gt;2 x Lexon Mino X bluetooth speakers&lt;/a&gt;: awesome USB C-rechargeable bluetooth speakers that can be paired together&lt;/li&gt;
&lt;li&gt;Logitech Spotlight Wireless Presentation Remote. &lt;a href="https://amzn.to/3Exru9H"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2.5mm to 3.5mm audio adapter. &lt;a href="https://amzn.to/3VlynBJ"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3VgRYmh"&gt;Yubico Yubikey 5C security key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3TWQr42"&gt;Yubico Yubikey 5C nano security key&lt;/a&gt; (always plugged in to laptop)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3EOkTb5"&gt;8Bitdo Zero 2 bluetooth Switch mini controller&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3UWDBEf"&gt;Anker USB C cables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;USB C adapters. &lt;a href="https://amzn.to/3i5MMU0"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3Vk8VMG"&gt;Røde Wireless Go wireless microphone system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3AySnsE"&gt;UGreen GaN 100W USB C charger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Hyper Drive USB hub. &lt;a href="https://amzn.to/3UUtboq"&gt;Updated version&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="my-remote-work-setup-for-a-month"&gt;My Remote Work Setup for a month&lt;/h2&gt;
&lt;p&gt;(&lt;a href="#sections"&gt;back to top&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what I bring with me when I need to be able to work while travelling continuously for a month. This setup lets me create videos, run livestreams, do programming work, and do presentations at conferences with minimal bulk and maximal functionality.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/bV6mR6UwE90?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Platypus soft bottle. &lt;a href="https://amzn.to/3TWaEH1"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3xX8y1A"&gt;Samsung Galaxy Active 2&lt;/a&gt;: my smartwatch&lt;/li&gt;
&lt;li&gt;Le Chivée USB C portable wireless charger for Samsung Galaxy Active 2 watch. &lt;a href="https://amzn.to/3AQWEaw"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3p6OHLJ"&gt;Bose QuietComfort Earbuds II&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3TVWNAn"&gt;Apple iPad Pro 11&amp;quot;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3GD7V2m"&gt;Apple Pencil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nedrelow.com/en-nl/products/magic-sleeve?variant=31842687189057"&gt;Nedrelow Magic Sleeve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;3 x &lt;a href="https://dispeldice.com"&gt;Dispel Dice&lt;/a&gt; d20s.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3tMKFax"&gt;Nuphy Air 60 keyboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Logitech Spotlight Wireless Presentation Remote. &lt;a href="https://amzn.to/3Exru9H"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3LSN2T4"&gt;Apple Magic Trackpad&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3GxVHId"&gt;2 x Lexon Mino X bluetooth speakers&lt;/a&gt;: awesome USB C-rechargeable bluetooth speakers that can be paired together&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3Vk8VMG"&gt;Røde Wireless Go wireless microphone system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ElQfW8"&gt;Roost laptop stand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZggnG"&gt;Macbook Pro 13&amp;quot; (2020)&lt;/a&gt;, 2,3 GHz Quad-Core Intel Core i7, 32 GB RAM - main laptop&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3TWQr42"&gt;Yubico Yubikey 5C nano security key&lt;/a&gt; (always plugged in to laptop)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CmuLH1"&gt;Sony A6600&lt;/a&gt; with &lt;a href="https://amzn.to/3xn3ZKD"&gt;Sigma 16mm f1.4&lt;/a&gt; lens (main shooter)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.peakdesign.com/pages/travel-tripod"&gt;Peak Design Travel tripod&lt;/a&gt;: Pricy, but the only full tripod I use these days. Excellent for travel!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3AWv0Zv"&gt;Elgato Cam Link 4K&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;USB C adapters. &lt;a href="https://amzn.to/3i5MMU0"&gt;Similar here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.peakdesign.com/products/tech-pouch/"&gt;Peak Design Tech Pouch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3UWDBEf"&gt;Anker USB C cables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/41Zm7KV"&gt;Lume Cube Panel Mini&lt;/a&gt; LED light.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ClU7Xz"&gt;Joby Gorillapod GripTight Smartphone Micro Tripod&lt;/a&gt;: tripod for Lume Cube&lt;/li&gt;
&lt;li&gt;Samsung X5 2TB SSD. &lt;a href="https://amzn.to/3VqqlIY"&gt;Better here - T7&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fls6Gu"&gt;Samsung Galaxy Z Fold 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;(not pictured, but since added) &lt;a href="https://amzn.to/3VL52Cc"&gt;Logitech POP Mouse&lt;/a&gt;: travel mouse, even though I hate that it requires an AA battery.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/travel-setup-20230430_220231.jpg" alt="Snapshot of my actual setup at a hotel in Frankfurt" loading="lazy" decoding="async"&gt;
&lt;/p&gt;</description></item><item><title>My WFH desk and office setup</title><link>https://nicolevanderhoeven.com/blog/20210712-my-wfh-desk-and-office-setup/</link><pubDate>Mon, 12 Jul 2021 21:18:55 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210712-my-wfh-desk-and-office-setup/</guid><description>&lt;p&gt;One of my favorite obsessions is watching or reading about the gear other people use. I&amp;rsquo;ve watched almost every &amp;ldquo;What&amp;rsquo;s in my tech bag&amp;rdquo; video on YouTube, it feels like, and I&amp;rsquo;m always prowling blog posts that detail what someone is using. In case there&amp;rsquo;s anybody out there that is as interested in these things as I am, here&amp;rsquo;s an overview of what my home office looks like and the gear that I use to make videos, write both blog posts and code, and generally mess around on the internet.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m only mentioning larger items of note that I use frequently.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/gear-study.JPG" alt="A photo of my home office on any given day" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Edit: After a reader suggested I provide links to these items, I&amp;rsquo;ve included them below, and some are affiliate links. You are under no obligation to click these links, but if you do, I get a tiny percentage of the sale. Thanks!&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="video-production-gear"&gt;Video production gear&lt;/h2&gt;
&lt;h3 id="not-on-desk"&gt;Not on desk&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jcSNva"&gt;Elgato green screen&lt;/a&gt; (long, black metal object on the left side of the photo, on the floor)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3ltqOdy"&gt;Canon 6D Mark II&lt;/a&gt; with a &lt;a href="https://amzn.to/3rTm6XK"&gt;Canon 16-35mm f/2.8L&lt;/a&gt; (upper left, on tripod with teleprompter)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rQ1kIl"&gt;Manfrotto 290 tripod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2TQA3sD"&gt;TeleprompterPAD iLight Pro 10&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jiNpH1"&gt;Neewer 5-in-1 reflective reflector set&lt;/a&gt; (gold, silver, white, black, and transparent)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fpnkVC"&gt;Neewer two-way rotating aluminum light stand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jen4K2"&gt;Manfrotto universal smartphone clamp&lt;/a&gt; (mounted on light stand above desk)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2WSA3tt"&gt;Neewer Camera Monopod&lt;/a&gt; with rotating column for panoramic shooting&lt;/li&gt;
&lt;li&gt;2 x &lt;a href="http://en.nanlite.com/products/detail-174"&gt;Nanlite PavoTube 6C II&lt;/a&gt; tubes on the shelves in the background&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="on-desk"&gt;On desk&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fub99P"&gt;Aputure AL-MC&lt;/a&gt; portable light (attached magnetically to the back of my external monitor)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3foIliT"&gt;Blue Yeti&lt;/a&gt; microphone&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3s46hxv"&gt;Elgato Key Light&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3yi8VSp"&gt;Canon M50&lt;/a&gt; (between my laptop and monitor screens, used as my main webcam)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3A7HKdv"&gt;Elgato multi-mount&lt;/a&gt; (holding up the M50)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fv26FA"&gt;Blackmagic Atem Mini&lt;/a&gt; video switcher&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZfVRW"&gt;Røde Wireless Go&lt;/a&gt; wireless microphone (main microphone, even for voice calls)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2Vcj84H"&gt;Samsung Portable SSD T5, 1TB&lt;/a&gt; (always connected to my laptop) the drive I edit off&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZggnG"&gt;Macbook Pro 13&amp;quot; (2020)&lt;/a&gt;, 2,3 GHz Quad-Core Intel Core i7, 32 GB RAM - main laptop&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="not-pictured-but-in-frequent-use"&gt;Not pictured but in frequent use&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CmuLH1"&gt;Sony A6600&lt;/a&gt; with &lt;a href="https://amzn.to/3xn3ZKD"&gt;Sigma 16mm f1.4&lt;/a&gt; lens (main shooter, used to take this shot)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/2Vc1A8R"&gt;Røde Wireless Go II&lt;/a&gt; two-person microphones (used as on-board camera mics)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3fowSA0"&gt;SwitchPod tripod&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="other-tech"&gt;Other tech&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3AbSrff"&gt;Synology DS920+&lt;/a&gt; 4 Bay NAS with 4 x &lt;a href="https://amzn.to/2VesKfg"&gt;WD RED 4TB&lt;/a&gt; drives (just outside the photo)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3iki732"&gt;Amazon Echo Dot&lt;/a&gt; (on the bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rZh0Ju"&gt;Google Wifi&lt;/a&gt; wireless extender (just outside photo)&lt;/li&gt;
&lt;li&gt;2 x &lt;a href="https://amzn.to/3jcWy3K"&gt;Nintendo Switch&lt;/a&gt; (on bookshelf; one &lt;a href="https://amzn.to/3jgXK6h"&gt;Animal Crossing edition&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3A8PBr8"&gt;Nintendo Lite&lt;/a&gt; Zacien and Zamazenta edition (on bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3xsRwFy"&gt;Amazon Kindle Oasis&lt;/a&gt; (bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/37fVvur"&gt;Amazon Kindle Paperwhite&lt;/a&gt; (bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://us.kobobooks.com/products/kobo-aura-one/"&gt;Kobo Aura One&lt;/a&gt; (bookshelf)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thomannmusic.com/risa_uke_solid_tenor.htm"&gt;Risa Electric Solid Tenor Ukulele&lt;/a&gt;, black (bookshelf)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="non-tech-gear"&gt;Non-tech gear&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wyrmwoodgaming.com/products/cherry-magnetic-dice-tower/"&gt;Wyrmwood Gaming Cherry magnetic dice tower bundle&lt;/a&gt; including dice tower, personal dice tray, and dice vault&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3jioG5p"&gt;Kimi Rainbow Acrylic kalimba&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CeHdIw"&gt;Visconti Homo Sapiens Lava&lt;/a&gt; Midi, steel age fountain pen (the only fountain pen of many that I use regularly)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/gear-desk.JPG" alt="A photo of my the things on my desk" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="non-video-gear-on-my-desk"&gt;Non-video gear on my desk&lt;/h2&gt;
&lt;p&gt;I already listed the video-related gear on my desk in the first section above, but here&amp;rsquo;s everything else.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3rOo8Iw"&gt;Samsung Galaxy Note 8&lt;/a&gt; (secondary phone, primarily for timelapses and overhead videos)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3lrYgkl"&gt;Samsung Galaxy Wireless Buds&lt;/a&gt;, rose&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CfKxmH"&gt;Samsung Galaxy Note 10+&lt;/a&gt; (primary phone)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3lotmtj"&gt;Apple Macbook Air 11&amp;quot;&lt;/a&gt; (2011, used as my presentation laptop)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3inWn6g"&gt;Apple Magic Trackpad&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3CgzR7E"&gt;Apple iPad Pro 11&amp;quot;&lt;/a&gt; (2020) tablet&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nizkeyboard.com/collections/peripherals/products/niz-2019-new-atom-68-ec-bluetooth-keyboard-rgb-or-non-rgb?variant=30346364059719"&gt;Niz Plum Atom 68&lt;/a&gt; electro-capacitive keyboard with &lt;a href="https://www.etsy.com/listing/902250753"&gt;Pikachu keycap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3xpYeMe"&gt;Logitech MX Master 3&lt;/a&gt; the best mouse I&amp;rsquo;ve used!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3lt8rFi"&gt;Bose NC700&lt;/a&gt; wireless noise-cancelling headphones&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ouraring.com/product/heritage-silver/step1"&gt;Oura&lt;/a&gt; health and sleep tracking ring&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Downloading files uploaded to Roam Research for use with Obsidian</title><link>https://nicolevanderhoeven.com/blog/20210602-downloading-files-from-roam/</link><pubDate>Wed, 02 Jun 2021 00:27:59 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210602-downloading-files-from-roam/</guid><description>&lt;p&gt;If you&amp;rsquo;ve recently transitioned from &lt;a href="https://roamresearch.com"&gt;Roam Research&lt;/a&gt; to &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;, you might run into a few issues, even after you&amp;rsquo;ve run the &amp;ldquo;Markdown format importer&amp;rdquo; core plugin. One of the most annoying ones that I ran into was that files uploaded to Roam (images, pdfs, etc.) are automatically uploaded to &lt;a href="https://firebase.google.com/"&gt;Google Firebase&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Roam assets are linked to in the Markdown files like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;![](https://firebasestorage.googleapis.com/v0/b/firescript-471a1.appspot.com/o/imgs%2Fapp%2FMyNotes%2FxRFXx7l-ZK.png?alt=media&amp;amp;token=1e03b680-5472-33dc-9b06-a127301af37f)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, since Roam relies on security by obscurity on this front, the links to your assets will still be accessible in your Obsidian vault, and they&amp;rsquo;ll still render correctly. However, I still think it&amp;rsquo;s worth downloading the files for three reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you don&amp;rsquo;t intend to use Roam anymore and cancel your subscription, they will likely delete those assets at some point, leaving you with a lot of broken links in Obsidian.&lt;/li&gt;
&lt;li&gt;Part of Obsidian&amp;rsquo;s appeal is that you own your assets. Not so if they&amp;rsquo;re on someone else&amp;rsquo;s server.&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t access Firebase files without an internet connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, instead, you want to download the contents of that link and replace it in your notes with something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;![](assets/newfile.png)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That way, you&amp;rsquo;ll have full control over all your assets, and you won&amp;rsquo;t be dependent on those assets being stored on Firebase by Roam.&lt;/p&gt;
&lt;p&gt;To fix this, I wrote a Python script that will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go through every file and every folder within a specified root vault directory&lt;/li&gt;
&lt;li&gt;Look for links to Google Firebase&lt;/li&gt;
&lt;li&gt;Download all assets from Firebase to a local folder (&lt;code&gt;/assets&lt;/code&gt; by default), using the timestamp and index number as a filename&lt;/li&gt;
&lt;li&gt;Replace the Firebase links in your vault with links to the newly downloaded files, accounting for PDFs, whose links are formatted differently in Roam as &lt;code&gt;{{ pdf: ... }}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before you run this script, I highly recommend backing up your vault or trying this out on a dummy vault.&lt;/p&gt;
&lt;script src="https://gist.github.com/nicolevanderhoeven/b25c97a8a68ea97e7bdf5ee674fdaec4.js"&gt;&lt;/script&gt;
&lt;p&gt;After downloading and saving the script, run &lt;code&gt;python3 downloadfirebase.py&lt;/code&gt; on your terminal.&lt;/p&gt;
&lt;p&gt;This worked really well for me, and I can breathe a little more easily now that my vault is self-contained.&lt;/p&gt;</description></item><item><title>End-to-End Guide for Load Testing</title><link>https://nicolevanderhoeven.com/blog/20210524-end-to-end-guide-for-load-testing/</link><pubDate>Mon, 24 May 2021 10:48:55 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210524-end-to-end-guide-for-load-testing/</guid><description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://abstracta.us/blog/performance-testing/load-testing-guide/"&gt;Abstracta blog&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;link rel="canonical" href="https://abstracta.us/blog/performance-testing/load-testing-guide/"&gt;
&lt;p&gt;If you have a public-facing web application, the odds are you should probably be thinking about load testing. Any tester can (and should) add load testing to their repertoire.&lt;/p&gt;
&lt;p&gt;Functional tests are often carried out with a single user&amp;rsquo;s experience of your application in mind, but what happens when hundreds or thousands of users are on your site at the same time? Load testing helps prepare an application for this eventuality by simulating that situation before the application goes live. That way, you have time to fix any issues you find, and there are no nasty surprises in production.&lt;/p&gt;
&lt;p&gt;In this guide, I&amp;rsquo;m going to walk you through the basics of the load testing process, which fits in closely with traditional functional testing phases as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Planning: &lt;em&gt;Why are we testing? What are we testing?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Writing test scripts: &lt;em&gt;How are we testing?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Execution: &lt;em&gt;Running test scripts against the application.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Analysis and reporting: &lt;em&gt;What happened, and what do we need to fix?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="planning-for-load-testing"&gt;Planning for load testing&lt;/h2&gt;
&lt;p&gt;During the planning phase, the team gets together and determines what load testing will involve, including the objectives, scope, resources needed, and test scenarios. The deliverable for this phase is a test plan, but that can be as formal or informal as the team requires.&lt;/p&gt;
&lt;p&gt;Planning for any testing is a team activity, and you&amp;rsquo;ll want to get input from all the stakeholders &amp;ndash; that&amp;rsquo;s anyone who is involved in or might be affected by the testing. The first thing you need to clarify is the reason for load testing.&lt;/p&gt;
&lt;h3 id="requirements"&gt;Requirements&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Why are we doing load testing?&lt;/em&gt; seems like a very simple question, but you may be surprised by the varying answers from your stakeholders. Their reasons may require different test scenarios, so it&amp;rsquo;s a good idea to agree on the collective objectives of this load testing cycle so that expectations are set accordingly. A requirement is a concise statement of how an application should behave in a given situation.&lt;/p&gt;
&lt;p&gt;Phrasing objectives as nonfunctional requirements (NFRs) is a good way to formalize them. An NFR defines the desired state of the application and can includes performance thresholds that the team decides on. For example: &lt;em&gt;Logging in as a registered user must take no more than 3 seconds (90th percentile response time)&lt;/em&gt; is a requirement based on response time. Good requirements are SMART (Specific, Measurable, Agreed Upon, Realistic, and Timely).&lt;/p&gt;
&lt;p&gt;Here are some other things that should be decided on during this phase:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;. &lt;em&gt;What are we testing?&lt;/em&gt; Deciding on a shared objective for load testing lets you narrow down what areas of the application need to be targeted. Make sure everyone is clear on what &lt;em&gt;won&amp;rsquo;t&lt;/em&gt; be tested.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/daria-nepriakhina-zoCDWPuiRuA-unsplash.jpg" alt="Post-its on a board for planning" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@epicantus?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Daria Nepriakhina&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/post-it?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Entry Criteria&lt;/strong&gt;. What do you need to begin? This is the time to start thinking about resources that are required for testing, including the number of people who need to be involved, the time it takes to carry out the testing, and environment requirements (including test data). You don&amp;rsquo;t always need a separate testing environment to do load testing, but if you do, that should be discussed up front so that the DevOps team knows you can&amp;rsquo;t begin without an environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workload Modeling&lt;/strong&gt;. A workload model is a set of characteristics that describe the load you&amp;rsquo;re attempting to simulate. If you want your load testing to simulate registered users accessing your site, what does that entail? This might require you to look through your site analytics to determine which pages are visited most often and which application features are most commonly used, or arrive at an estimate if analytics are not available. Are most users visiting from their laptops at lunch on work days, or do they use your site from mobile devices after work? Are most of them located in the US or in Europe? You&amp;rsquo;ll want your scripts to match real load as much as possible, so take note of key characteristics that might have an effect on the traffic your application experiences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Server monitoring&lt;/strong&gt;. Which application components will be involved? You want to have a plan in place to have monitoring, alerts, and logs set up on every server that will be tested by your scripts. If your load testing tool shows performance degradation, you want to be able to have information to look through on the relevant servers to troubleshoot that issue. Keep in mind that any sort of monitoring adds some resource overhead, so decide as a team what information you absolutely need to track.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Test Scenarios&lt;/strong&gt;. Which situations do you want to recreate? A test scenario objectively describes the situation to be simulated. Do you want to recreate the load profile of a typical day, or one from a very high-traffic period, such as during a sale? Do you want to recreate a production outage? You can use analytics data to come up with parameters for your each test scenario such as duration, number of concurrent users or requests, ramp up period (how quickly the application went from 0 users to your highest number of users), or how &amp;ldquo;spiky&amp;rdquo; the load needs to be (how the number of requests per second fluctuates throughout the test).&lt;/p&gt;
&lt;p&gt;Everything discussed in this phase should be written in some sort of a load testing plan, which could be a formal, multi-page document, an epic with many user stories, or a bunch of Trello cards. Use the tools your team already uses. The format isn&amp;rsquo;t important; formalizing the outcome of the discussions is what counts. The test plan makes sure everyone has the same intent with regard to the testing.&lt;/p&gt;
&lt;h2 id="writing-load-testing-scripts"&gt;Writing load testing scripts&lt;/h2&gt;
&lt;p&gt;Now it&amp;rsquo;s time to start writing scripts! You&amp;rsquo;ve already front-loaded a lot of the important decisions to the planning phase, so all that&amp;rsquo;s left to do in this step is to implement the plan in the form of load testing scripts.&lt;/p&gt;
&lt;h3 id="tool-selection"&gt;Tool selection&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/tools_on_market.png" alt="Different load testing tools on the market" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;There are many load testing tools available on the market, and the reality is that you don&amp;rsquo;t always get to be the one to choose what you use. Often, companies have already made agreements with tool vendors for licenses before you begin your load testing, and it would be financially infeasible to switch to another tool. However, if you&amp;rsquo;re lucky enough that you can choose the load testing tool, here are a few considerations for assessing different tools:&lt;/p&gt;
&lt;h4 id="resources-and-costs"&gt;Resources and costs&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;How much does it cost? Does the pricing model allow for scaling usage up and down as necessary?&lt;/li&gt;
&lt;li&gt;Should you &lt;a href="https://k6.io/what-to-consider-when-building-or-buying-a-load-testing-solution/"&gt;build or buy&lt;/a&gt; an end-to-end load testing solution?&lt;/li&gt;
&lt;li&gt;What technical skills or experience with tools does your team have? Who will be doing the testing?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="features"&gt;Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Does the load testing tool support the protocols and technologies used by the application?&lt;/li&gt;
&lt;li&gt;How easy would it be for the team to learn this tool and/or scripting language?&lt;/li&gt;
&lt;li&gt;Does the tool make it easy for multiple people to collaborate on the same script?&lt;/li&gt;
&lt;li&gt;Does it integrate with other tools in the testing stack (for test management, test result analysis, server monitoring, team communication)?&lt;/li&gt;
&lt;li&gt;Could you add this tool to your CI/CD pipeline?&lt;/li&gt;
&lt;li&gt;Does the tool have an easy way of setting thresholds or SLOs for performance?&lt;/li&gt;
&lt;li&gt;Are the test results displayed in usable reports, or does the tool integrate with existing data visualization tools you are using?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="support"&gt;Support&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;If something goes wrong, does customer support cost extra?&lt;/li&gt;
&lt;li&gt;Is there ample documentation available online for getting started without help?&lt;/li&gt;
&lt;li&gt;Is there a community of users of the tool whose experiences you could learn from?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="scaling-up-load-testing"&gt;Scaling up load testing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;How many virtual users or requests per second can a single load generator simulate using the tool?&lt;/li&gt;
&lt;li&gt;How many load generators would you need if you used this tool?&lt;/li&gt;
&lt;li&gt;Can the tool handle the amount of test data required by your tests?&lt;/li&gt;
&lt;li&gt;Can you execute tests from different geographical locations, depending on your the location of your users?&lt;/li&gt;
&lt;li&gt;Does the tool allow you to generate load from the cloud?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These questions are not exhaustive, but you can use them as a starting point for your comparisons of load testing tools.&lt;/p&gt;
&lt;p&gt;For this guide, I&amp;rsquo;m going to outline a basic script in &lt;a href="https://k6.io"&gt;k6&lt;/a&gt; for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s open-source, so anyone reading this can download the tool and the script and try it out for themselves;&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s written in JavaScript, which makes it easy to learn for front-end developers or testers who have used tools like Cypress, Puppeteer, or Playwright.&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s capable of generating &lt;a href="https://k6.io/docs/testing-guides/running-large-tests/"&gt;tens of thousands of users&lt;/a&gt; on a single machine, so it&amp;rsquo;s cost-effective even with load ramped up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, the principles I discuss will be applicable to other tools. I highly recommend you conduct your own proof of concepts with several tools to come up with a tool that fits your particular situation, test scenarios, and company.&lt;/p&gt;
&lt;h3 id="installation-and-setup"&gt;Installation and setup&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re on a Mac, the easiest way to install k6 is to use Brew. Run &lt;code&gt;brew install k6&lt;/code&gt; from your terminal, and you&amp;rsquo;ll be ready to go in a few seconds.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re on a different operating system, &lt;a href="https://k6.io/docs/getting-started/installation/"&gt;check here for installation instructions&lt;/a&gt; that apply to you.&lt;/p&gt;
&lt;h3 id="a-basic-k6-test-script"&gt;A basic k6 test script&lt;/h3&gt;
&lt;p&gt;A barebones k6 test script looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6/http&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;https://test.k6.io&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This script sends an HTTP GET request for the URL &lt;code&gt;https://test.k6.io&lt;/code&gt;, which is a simple test site. If you copy that code to your favorite text editor and save it as &lt;code&gt;test.js&lt;/code&gt;, you can then run it with the command &lt;code&gt;k6 run test.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll get something like this in response:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/k6-supersimple-run.png" alt="A sample run of a simple local test in k6" loading="lazy" decoding="async"&gt;
&lt;em&gt;A sample run of a simple local test in k6&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The output above means that you&amp;rsquo;ve executed 1 iteration of your script with 1 virtual user from your local machine. The test run consisted of 1 HTTP request, which had a response time of 128.2 ms (&lt;code&gt;http_req_duration&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It might be tempting to start scaling up the test at this point, increasing the number of virtual users and the duration of the script. But there&amp;rsquo;s one problem with the script as it is: it&amp;rsquo;s not realistic.&lt;/p&gt;
&lt;h3 id="making-a-test-script-realistic"&gt;Making a test script realistic&lt;/h3&gt;
&lt;p&gt;The purpose of a load testing script is to simulate a real user who accesses your site. In most cases, that involves more than the simple GET request from the script above. Load testing scripts don&amp;rsquo;t have to mimic a real user &lt;em&gt;exactly&lt;/em&gt;, but they do have to be close enough so as to apply the same amount, shape, and distribution of the load on your application server. If your load testing scripts aren&amp;rsquo;t realistic enough, you may get false positives during testing and discover too late that there is a performance issue in an application component that your scripts didn&amp;rsquo;t test.&lt;/p&gt;
&lt;p&gt;Here are some ways to make your scripts more realistic:&lt;/p&gt;
&lt;h4 id="add-static-resources"&gt;Add static resources&lt;/h4&gt;
&lt;p&gt;If a page has static resources like images and scripts that are retrieved by a browser automatically, make sure your script retrieves those too, as they will have an impact on the response times you get. Consider excluding static resources fetched from third-party providers unless you&amp;rsquo;ve gotten their permission to run a load test on their servers.&lt;/p&gt;
&lt;h4 id="set-cache-and-cookie-behavior"&gt;Set cache and cookie behavior&lt;/h4&gt;
&lt;p&gt;Review your load testing tool&amp;rsquo;s default cache and cookie management settings and change them, if necessary, to match the scenario you want to test. First-time visitors to a site won&amp;rsquo;t have resources cached locally, but repeat visitors may be retrieving resources from the cache.&lt;/p&gt;
&lt;h4 id="add-think-time"&gt;Add think time&lt;/h4&gt;
&lt;p&gt;Real users don&amp;rsquo;t usually fetch the same URL over and over again without stopping. They navigate to your site, then spend some time reading the contents of the homepage before they interact with the page again. Think time is the amount of time that a user spends “thinking”— that is, the delay between requests. Adding a think time that varies between 1 and 5 seconds, for example, prevents the application server from getting artificially bombarded by requests in a way that wouldn&amp;rsquo;t occur in production.&lt;/p&gt;
&lt;h4 id="using-different-user-paths"&gt;Using different user paths&lt;/h4&gt;
&lt;p&gt;A user path is the flow of how a user interacts with the application. What pages do they visit? What information do they type in? So far, you&amp;rsquo;re only testing one request, but perhaps your users may be performing other actions. The workload model that you put together in the Planning phase will come into play here. Other actions like logging in or paying for items in a cart exercise different parts of an application than browsing does, so the load testing script should reflect the most common user flows through your system. In k6, this is done by adding scenarios, but other tools may call them thread groups or execution groups.&lt;/p&gt;
&lt;h4 id="adding-test-data"&gt;Adding test data&lt;/h4&gt;
&lt;p&gt;In the same way that users don&amp;rsquo;t (typically) search for the same keyword over and over again and don&amp;rsquo;t all log into the same test account, a script shouldn&amp;rsquo;t use the same data repeatedly. This behavior could cause responses to be cached, leading to misleadingly quick response times, or increased error rates, in the case of multiple virtual users attempting to log into the same account. To prevent that, modify your script to take data from a CSV file instead, letting you add some natural variation.&lt;/p&gt;
&lt;h4 id="setting-test-parameters"&gt;Setting test parameters&lt;/h4&gt;
&lt;p&gt;Test parameters are the main characteristics of the test and include the number of users, ramp up, duration, and ramp down. They control the shape of the user load, which you can see when you plot the number of virtual users over time during the test:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/k6-vusonly.png" alt="The number of virtual users over time during a load test, as graphed in k6" loading="lazy" decoding="async"&gt;
&lt;em&gt;The number of virtual users over time during a load test, as graphed in k6&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ramp up is a period of time during which virtual users are gradually added, usually at the start of the test, and ramp down is when they are gradually stopped. For most applications, ramps are more realistic than all users clicking a link at exactly the same time.&lt;/p&gt;
&lt;h4 id="setting-failure-criteria"&gt;Setting failure criteria&lt;/h4&gt;
&lt;p&gt;Part of simulating real users is determining what constitutes failures in user experience. You can incorporate failure criteria into scripts by adding response verifications, checks, and thresholds (such as those for response time or error rate). Building these into the script is a good practice to get into, because it defines the limits of acceptable performance from the beginning.&lt;/p&gt;
&lt;p&gt;The suggestions in this section are for general use, but there are many situations where taking the opposite approach would be more realistic. Spend some time thinking about what is &amp;ldquo;realistic&amp;rdquo; for your application and test scenario.&lt;/p&gt;
&lt;p&gt;For your reference, &lt;a href="https://gist.github.com/nicolevanderhoeven/fb74cd9769c8abf95eeb8765a49398cb"&gt;here&amp;rsquo;s a k6 script&lt;/a&gt; that started from the basic script I showed earlier, but has been modified to be more realistic. That link includes a CSV file with login credentials.&lt;/p&gt;
&lt;h2 id="executing-load-tests"&gt;Executing load tests&lt;/h2&gt;
&lt;p&gt;In the course of writing the test script, you probably would have already executed small verification tests, called shakeout tests. It&amp;rsquo;s a good idea to start with these shakeout tests on a new environment to make sure that both your test artifacts (your script and any other data files) and the environment are ready for full testing.&lt;/p&gt;
&lt;h3 id="running-the-test"&gt;Running the test&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;ve been following along with the k6 script so far, all you need to do to run a load test is to execute the following command:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;k6 run test.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This command will run the k6 script according to the test parameters set within it. The test will be executed locally. This is best used for shakeout tests, or if you prefer to run your load test on your own infrastructure.&lt;/p&gt;
&lt;h3 id="realism-in-test-execution"&gt;Realism in test execution&lt;/h3&gt;
&lt;p&gt;One way can make executing load tests realistic is by matching the location of load generators to the physical location of your users. When you run a load test on machines in your data center, in the same network as your application servers, you will likely see response times that are significantly faster than your users would see. These false positives can look good on the report, but they don&amp;rsquo;t help you measure real user experience.&lt;/p&gt;
&lt;p&gt;Running on the cloud is an easy way to make tests more realistic, especially if most of your end users are external to your organization.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re following along with the k6 script, you can &lt;a href="https://app.k6.io/account/register"&gt;sign up for an account&lt;/a&gt; with k6 Cloud (the first 50 tests are free). Since k6 itself is open source, you can also run it on your own cloud infrastructure, but using k6 Cloud is the most convenient way to get started.&lt;/p&gt;
&lt;p&gt;Once you have an account, &lt;a href="https://app.k6.io/account/api-token"&gt;copy your API token from here&lt;/a&gt;. Then, run this command in your terminal to give your local k6 installation permission to run on your account:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;k6 login cloud --token &amp;lt;YOUR_K6_CLOUD_API_TOKEN&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Once you are successfully authenticated, you can continue to run tests locally using &lt;code&gt;k6 run&lt;/code&gt;, but you now have the option of running on the cloud as well:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;k6 cloud test.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;k6 will then begin to execute your load test, and you&amp;rsquo;ll see a screen like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/k6-cloud-run-output.png" alt="Running a k6 test on the cloud" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Note that the &lt;code&gt;execution&lt;/code&gt; mode is now &lt;code&gt;cloud&lt;/code&gt;, to remind you where you&amp;rsquo;re running the test.&lt;/p&gt;
&lt;p&gt;By default, your test will run on the k6 AWS account, in the Ashburn region. However, you can override this and specify other regions, including percentages of your virtual user to run on each, using &lt;a href="https://k6.io/docs/cloud/creating-and-running-a-test/cloud-tests-from-the-cli#cloud-execution-options"&gt;cloud execution options&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="analyzing-load-testing-results-and-reporting"&gt;Analyzing load testing results and reporting&lt;/h2&gt;
&lt;p&gt;Although I&amp;rsquo;m listing analysis as a different step in the process from execution, the two often (and should) overlap. The output from k6 in the screenshot above contains a link to the dashboard on k6 Cloud, so that you can see the results of the test in real time, as the test is running. Monitoring the test in real time lets you immediately spot issues as they arise, and, if necessary, stop the test to fix them. If the test is being executed by multiple load generators, having a consolidated dashboard is helpful in giving you an overview of the entire test.&lt;/p&gt;
&lt;p&gt;![An example of the results after a load test in k6 Cloud](/assets/Finished k6 run.png)
&lt;em&gt;An example of the results after a load test in k6 Cloud&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is also a good time to monitor the health of the application servers and the load generator servers, which you set up monitoring tools for during the planning phase.&lt;/p&gt;
&lt;p&gt;A load test is only useful when it&amp;rsquo;s providing information about the application. If errors occur in large quantities, or if the response time makes it clear that a certain component has failed to handle the load, don&amp;rsquo;t be afraid to stop the test. Your objective is to get the information you need to suggest performance improvements and solutions for any bottlenecks you find.&lt;/p&gt;
&lt;p&gt;Often, you&amp;rsquo;ll run into issues you can&amp;rsquo;t solve or even identify during a test. That&amp;rsquo;s why it&amp;rsquo;s useful to explore the data that you have after a test. k6 lets you create shareable links to dashboards so that you can involve relevant engineering teams who may be able to help you understand what happened and why. &lt;a href="https://app.k6.io/runs/public/f6ad87cec7a24a8c82e46c816e67c4b3"&gt;Here&amp;rsquo;s an example of what that dashboard looks like&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some projects require formal documentation of test execution by way of a Test Summary Report, but in more agile teams, it may be enough to share the test results dashboard and some supplementary information about what type of tests were run and what problems were encountered.&lt;/p&gt;
&lt;p&gt;The most important part of load testing isn&amp;rsquo;t the execution itself; it&amp;rsquo;s what you do with the results. Communicating results clearly to stakeholders will make it more likely that performance issues are fixed.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;The phases I&amp;rsquo;ve outlined here are a good guide for anyone starting with load testing, but I always recommend that you take what works for your situation and leave the rest. In reality, the phases overlap with each other considerably, especially in more modern engineering cycles.&lt;/p&gt;
&lt;p&gt;Another activity I&amp;rsquo;ve not covered here, but one that I recommend, is adding load testing to your CI/CD pipeline. This continuous testing approach involves &lt;a href="https://k6.io/docs/testing-guides/automated-performance-testing/"&gt;automating load testing&lt;/a&gt; so that it occurs in regularly, in parallel with other development and testing activities. Continuous testing is a great way to get more value out of load testing, and it ensures that performance is front-of-mind for everyone on the application team.&lt;/p&gt;
&lt;p&gt;Happy testing!&lt;/p&gt;</description></item><item><title>How I Use Obsidian at Work</title><link>https://nicolevanderhoeven.com/blog/20210518-how-i-use-obsidian-at-work/</link><pubDate>Tue, 18 May 2021 22:33:18 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210518-how-i-use-obsidian-at-work/</guid><description>&lt;p&gt;Note-taking and personal knowledge management get talked about in academic circles, by both students and teachers. But there&amp;rsquo;s not much said about its use outside of the academe.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/graphview-transparent.png" alt="Obsidian for work" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll admit, due to my job description, I may be in a better position than most to take advantage of note-taking in a professional context. My background is in software performance testing. My job for several years has been to make sure that applications are prepared to withstand traffic from many users accessing it, usually by writing code to simulate that traffic before it&amp;rsquo;s released to real users. Currently, though, I&amp;rsquo;m a Developer Advocate at &lt;a href="https://k6.io"&gt;k6.io&lt;/a&gt;, which means a significant part of my job now is sharing with people what I&amp;rsquo;ve learned.&lt;/p&gt;
&lt;p&gt;I consider myself a knowledge worker: a significant part of my job depends on how quickly I learn new technologies &lt;em&gt;and&lt;/em&gt; how clearly I can impart what I&amp;rsquo;ve learned to others. I&amp;rsquo;d argue, though, that most people who work in a field as fast-moving as tech are knowledge workers too. It&amp;rsquo;s naive to think we won&amp;rsquo;t one day forget some skills we&amp;rsquo;ve learned, even if we were using them daily at one point. Yet nobody talks about note-taking.&lt;/p&gt;
&lt;p&gt;Recently, my note-taking and personal knowledge management tool of choice has been &lt;a href="https://obsidian.md"&gt;Obsidian.md&lt;/a&gt;. I have no affiliation with the Obsidian team, and in fact I&amp;rsquo;m an investor of &lt;a href="https://roamresearch.com"&gt;a competing product&lt;/a&gt;. Here are some ways I use Obsidian at work that might help you understand why Obsidian is my daily driver.&lt;/p&gt;
&lt;h2 id="my-setup"&gt;My setup&lt;/h2&gt;
&lt;p&gt;I have multiple Obsidian vaults, and each of them is a version-controlled &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt; repository, hosted on &lt;a href="https://github.com"&gt;GitHub&lt;/a&gt;. I keep my main Obsidian vault in a &lt;a href="https://dropbox.com"&gt;Dropbox&lt;/a&gt; for extra version control and as a backup. Just for overkill, I also use &lt;a href="https://obsidian.md/sync"&gt;Obsidian Sync&lt;/a&gt; as another backup and as the easiest way to get access to my Obsidian vault on my iPad Pro and my Android mobile.&lt;/p&gt;
&lt;p&gt;I use &lt;a href="https://obsidian.md/publish"&gt;Obsidian Publish&lt;/a&gt; to make some of my notes &lt;a href="https://notes.nicolevanderhoeven.com"&gt;publicly available&lt;/a&gt; on a custom domain directly from my vault, as an experiment in learning in public that&amp;rsquo;s gone pretty well so far.&lt;/p&gt;
&lt;p&gt;This setup isn&amp;rsquo;t free. All up, I&amp;rsquo;m giving Obsidian about US $16 a month. However, I want to point out that Obsidian itself is free for personal use, and if mobile access, cloud sync, and publishing your notes online aren&amp;rsquo;t important to you, you can really go a long way with using it for free.&lt;/p&gt;
&lt;h2 id="processing-information"&gt;Processing information&lt;/h2&gt;
&lt;p&gt;Remaining relevant in tech pretty much requires that you learn a lot, learn quickly, and enjoy the hell out of it. Luckily, Obsidian is pretty great at this.&lt;/p&gt;
&lt;h3 id="consume-and-collect"&gt;Consume and collect&lt;/h3&gt;
&lt;p&gt;I consume a lot of content, and in different formats: articles, books, videos, presentations, podcasts are all great sources of free education. I initially capture my notes on them in different ways, but they all lead back to using &lt;a href="https://readwise.io/"&gt;Readwise&lt;/a&gt;, which I use as a collector and sorter of all these notes. Then, I import data from Readwise to Obsidian using a process I describe in more detail &lt;a href="https://nicolevanderhoeven.com/blog/20210206-readwise-to-obsidian/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For now, suffice it to say that all these notes and highlights end up in Obsidian for processing. &lt;a href="https://notes.nicolevanderhoeven.com/Presentation/In+the+kitchen+-+a+sprinkle+of+fire+and+chaos"&gt;Here&amp;rsquo;s an example of my notes during a presentation&lt;/a&gt; (called &lt;em&gt;In the kitchen - a sprinkle of fire and chaos&lt;/em&gt; by &lt;a href="https://www.anammedina.com/"&gt;Ana Medina&lt;/a&gt;, about chaos engineering).&lt;/p&gt;
&lt;p&gt;When trying to learn something, many people stop at this phase. They&amp;rsquo;ve read the thing, they&amp;rsquo;ve highlighted passages from the thing, and now they think they&amp;rsquo;ve learned the thing. That hasn&amp;rsquo;t been my experience, because I forget many things I consume shallowly.&lt;/p&gt;
&lt;h3 id="process-iteratively"&gt;Process iteratively&lt;/h3&gt;
&lt;p&gt;Then, I process notes iteratively, first restating things into my own words and then creating my own notes about concepts I&amp;rsquo;ve learned. Each time I do that, I try to identify key concepts and create backlinks for them, which in Obsidian just means typing &lt;code&gt;[[Chaos engineering]]&lt;/code&gt;, with a topic between double brackets. If I click on that new link, it creates a page for it. Over time, I end up with a page &lt;a href="https://notes.nicolevanderhoeven.com/Chaos+Engineering"&gt;like this one on Chaos Engineering&lt;/a&gt; that is a consolidation of what I&amp;rsquo;ve learned.&lt;/p&gt;
&lt;p&gt;Interestingly, if I go to the Chaos Engineering page, I can also see all the instances I&amp;rsquo;ve linked to it from other notes:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/linked-mentions.png" alt="Linked mentions in Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;What if I didn&amp;rsquo;t have the foresight to link to Chaos Engineering from an article I read? Obsidian also tracks &lt;em&gt;unlinked&lt;/em&gt; mentions as well. I can see all the times I&amp;rsquo;ve used the words &amp;ldquo;chaos engineering&amp;rdquo; in other notes, even if I haven&amp;rsquo;t created formal links using &lt;code&gt;[[Chaos engineering]]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Over time, things I&amp;rsquo;m interested in start to emerge this way, and topics grow deeper and more detailed, with branches of pages spinning off from it.&lt;/p&gt;
&lt;h3 id="situate-and-solidify"&gt;Situate and solidify&lt;/h3&gt;
&lt;p&gt;Then, I try to think about where this new piece of knowledge fits into the hierarchy of things I&amp;rsquo;m interested in. This is less about trying to classify the world and more about putting it somewhere on &lt;em&gt;my personal&lt;/em&gt; map of interests so that I&amp;rsquo;m more likely to use that knowledge later. For me, chaos engineering fit into my page on &lt;a href="https://notes.nicolevanderhoeven.com/Nonfunctional+testing"&gt;Nonfunctional testing&lt;/a&gt;. Doing so helps me pit it against other things I&amp;rsquo;ve classified as forms of nonfunctional testing and think critically about whether it belongs there, or whether other things I already know contradict it in some way. Sometimes I&amp;rsquo;ll make notes about my reasoning for classification, such as &lt;a href="https://notes.nicolevanderhoeven.com/Chaos+engineering+is+a+testing+discipline"&gt;this note on why I think chaos engineering is a testing discipline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;None of this is set in stone. I&amp;rsquo;m always changing notes around as I learn more things that change my mind. For example, I&amp;rsquo;m considering merging my nonfunctional testing and functional testing pages after a conversation where someone questioned whether it wasn&amp;rsquo;t a misnomer to call application performance &amp;ldquo;nonfunctional&amp;rdquo;. The point is to create a living, breathing second brain that helps me make connections between concepts and remember what I&amp;rsquo;ve learned.&lt;/p&gt;
&lt;p&gt;Obsidian&amp;rsquo;s built-in graph view illustrates this point nicely:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/graph_view-20210518.png" alt="Built-in graph view" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This is my main Obsidian vault as of the time of this writing - all 5443 notes of it. The graph view plots every note and the connections between notes, so I&amp;rsquo;ve ended up with a slick visual representation of what I&amp;rsquo;ve been thinking and writing about. The graph view is actually &lt;em&gt;functional&lt;/em&gt;, too&amp;ndash; I can assign colors to nodes based on the directory they&amp;rsquo;re in, their tags, words they can contain, and a lot of other criteria. Because each node is sized according to how many links lead to it, this view makes it easy to see clusters in things I&amp;rsquo;ve thought or written about.&lt;/p&gt;
&lt;h3 id="publish"&gt;Publish&lt;/h3&gt;
&lt;p&gt;One of the advantages of Obsidian over something like Notion or Roam Research is how easy it is to publish notes from it.&lt;/p&gt;
&lt;h4 id="obsidian-publish-paid"&gt;Obsidian Publish (paid)&lt;/h4&gt;
&lt;p&gt;There&amp;rsquo;s the paid Publish service, which I&amp;rsquo;ve subscribed to for US $8 a month. After &lt;a href="https://notes.nicolevanderhoeven.com/Using+a+custom+domain+with+Obsidian+Publish"&gt;setting it up to publish on my own domain&lt;/a&gt; rather than the default at &lt;code&gt;publish.obsidian.md&lt;/code&gt;, going from a note to a published post is as easy as clicking the Publish icon, selecting the notes to add, delete, or modify, and then selecting Publish.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/publish-dialog.png" alt="Dialog for Obsidian Publish" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h4 id="open-folder-as-vault"&gt;Open folder as vault&lt;/h4&gt;
&lt;p&gt;If you don&amp;rsquo;t want to pay for Publish, Obsidian still saves all files locally in Markdown (&lt;code&gt;.md&lt;/code&gt;) format. This is the same format that many static site generators take for content. This site that you&amp;rsquo;re reading this on is generated using &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt;, so everything written here is kept as a Markdown file before it&amp;rsquo;s turned into HTML. Since an &amp;ldquo;Obsidian vault&amp;rdquo; is just a folder with Markdown files, I can just open the directory containing the files for my site and write blog posts in Obsidian with no noticeable difference.&lt;/p&gt;
&lt;p&gt;At work, we happen to store our main &lt;a href="https://k6.io/docs/"&gt;product documentation&lt;/a&gt; as Markdown files in a &lt;a href="https://github.com/k6io/docs"&gt;GitHub repo&lt;/a&gt;, which make it perfect for opening in Obsidian as well. I prefer to write documentation in Obsidian over VSCode for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I can install plugins, like &lt;a href="https://github.com/tgrosinger/advanced-tables-obsidian"&gt;Advanced Tables&lt;/a&gt; to make Markdown tables easier to deal with, create templates to standardize the format of notes you make often, see an outline of what you&amp;rsquo;re writing based on Markdown headings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/outlines-obsidian.png" alt="See an outline of your current note" loading="lazy" decoding="async"&gt;
&lt;em&gt;Outline core plugin for Obsidian&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When I want to link to another page, it&amp;rsquo;s easier to just hit CMD + O to search through existing notes instead of finding it on GitHub.&lt;/li&gt;
&lt;li&gt;I can choose an Obsidian theme that matches the theme of the published site. &lt;a href="https://github.com/ishgunacar/gitsidian"&gt;This &amp;ldquo;Gitsidian&amp;rdquo; theme&lt;/a&gt; has an Edit view in dark mode (my preference) but a rendered Preview mode that looks more like GitHub&amp;rsquo;s Markdown format, so I can see what it will actually look like for users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://github.com/ishgunacar/gitsidian/raw/master/showcase.png" alt="Gitsidian theme preview for Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;When I&amp;rsquo;m done, I can still jump to my terminal and push my change to GitHub as normal.&lt;/p&gt;
&lt;h4 id="obsidian-uris"&gt;Obsidian URIs&lt;/h4&gt;
&lt;p&gt;Another cool feature is the fact that every note in Obsidian has what&amp;rsquo;s called an Obsidian URI - it&amp;rsquo;s a link that opens up a note within a specified vault. The URI looks like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;obsidian://open?vault=docs&amp;amp;file=src%2Fdata%2Fmarkdown%2Fdocs%2F03%20cloud%2F02%20Analyzing%20Results%2F02%20Performance%20Insights&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Adding it and publishing that to internal documentation means I can switch easily from browsing on GitHub to modifying the same page in Obsidian on my local machine, which is a great little bonus.&lt;/p&gt;
&lt;h4 id="embedding-published-work"&gt;Embedding published work&lt;/h4&gt;
&lt;p&gt;After publishing, I use iframes in Obsidian to update my notes with the final result. Here&amp;rsquo;s a video on YouTube about chaos engineering that my colleague &lt;a href="https://simme.dev"&gt;Simme&lt;/a&gt; and I planned out collaboratively on Obsidian, embedded into Obsidian:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/embedded-youtube-iframe.png" alt="Embedding a video in an iframe" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The video plays in Obsidian with all the same controls. The Obsidian desktop app is an Electron app, so it behaves just like a normal browser for playing back the video. To embed the video, I just used the Share &amp;gt; Embed code on YouTube and pasted it straight into Obsidian:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;iframe&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;width&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;560&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;height&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;315&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;https://www.youtube.com/embed/gVwJZPo30rk&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;title&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;YouTube video player&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;frameborder&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;allow&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;allowfullscreen&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;iframe&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="meetings"&gt;Meetings&lt;/h2&gt;
&lt;p&gt;I take meeting notes to help me focus on the meeting, and I create templates for recurring meetings. Templates let you define the format of a note. Once created, you can insert templates into notes with a keyboard shortcut (I set mine to &lt;code&gt;CMD + ;&lt;/code&gt;). Here&amp;rsquo;s an example of what that looks like, with the template on the left and the inserted template on the right:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-meeting-templates.png" alt="Template (left) and inserted template in a new note (right) in Obsidian" loading="lazy" decoding="async"&gt;
&lt;em&gt;Template (left) and inserted template in a new note (right) in Obsidian&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You may notice that the frontmatter includes &lt;code&gt;{{date}}&lt;/code&gt; in the template, which gets automatically converted to today&amp;rsquo;s date when the template is used.&lt;/p&gt;
&lt;p&gt;If anyone asks for a copy of my meeting notes, that&amp;rsquo;s no problem&amp;ndash; any note can be exported to Obsidian by clicking on Options &amp;gt; Export to PDF.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-pdf-export.png" alt="Options for exporting a note to a PDF in Obsidian" loading="lazy" decoding="async"&gt;
&lt;em&gt;Options for exporting a note to a PDF in Obsidian&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t want to actually publish any real meeting notes, so here&amp;rsquo;s what my note on the chaos engineering process looks like &lt;a href="https://nicolevanderhoeven.com/assets/The%20process%20of%20chaos%20engineering.pdf"&gt;as a PDF&lt;/a&gt;, straight from Obsidian. And here&amp;rsquo;s what it looks like &lt;a href="https://notes.nicolevanderhoeven.com/The+process+of+chaos+engineering"&gt;as a note&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="presentations"&gt;Presentations&lt;/h2&gt;
&lt;h3 id="the-easier-way"&gt;The easier way&lt;/h3&gt;
&lt;p&gt;Obsidian has a presentation mode! It&amp;rsquo;s basic, but it&amp;rsquo;s functional. You can create a presentation really easily by adding &lt;code&gt;---&lt;/code&gt; between paragraphs of text to delineate slides, right within a note. Here&amp;rsquo;s what that looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-slides-markdown.png" alt="Creating a presentation from Markdown notes" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Then you can click Options &amp;gt; Start presentation, and you get something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-slides.png" alt="An Obsidian presentation" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;A little plain, but given the minimal amount of effort to &amp;ldquo;convert&amp;rdquo; a note into a presentation, it&amp;rsquo;s pretty awesome.&lt;/p&gt;
&lt;h3 id="the-harder-way"&gt;The harder way&lt;/h3&gt;
&lt;p&gt;Sometimes the basic slides just don&amp;rsquo;t cut it, and it can be handy to be able to publish the slides in presentation format (since publishing them straight from Obsidian Publish would only publish the Markdown version). For those cases, I&amp;rsquo;ve figured out a way to do just that while still keeping my slides as Markdown files in my vault. &lt;a href="https://nicolevanderhoeven.com/blog/20210302-presentation-slides-as-code/"&gt;Here&amp;rsquo;s a whole post on how to do that&lt;/a&gt;. This is definitely more complicated, but at the end of it, you get a nice page &lt;a href="https://slides.nicolevanderhoeven.com/2021-load-tests-as-code/#/"&gt;like this&lt;/a&gt; to send to attendees after a presentation. (&lt;em&gt;Tip: Hit &lt;code&gt;s&lt;/code&gt; to see the speaker notes while on those slides.&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210302-reveal-hugo-sample.png" alt="A sample custom slide using revealJS with Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="task-management"&gt;Task management&lt;/h2&gt;
&lt;p&gt;I use a few community plugins to keep track of things I want to.&lt;/p&gt;
&lt;h3 id="kanban"&gt;Kanban&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://github.com/mgmeyers/obsidian-kanban"&gt;Kanban plugin&lt;/a&gt; turns Markdown task lists into full-on Kanban boards.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-kanban.png" alt="Kanban plugin in action" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;On the left is the markdown note, and the on the right is the rendered Kanban board. You can customize the titles of the lists that are there, and you can add due dates to each task.&lt;/p&gt;
&lt;h3 id="calendar"&gt;Calendar&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://github.com/liamcain/obsidian-calendar-plugin"&gt;Calendar plugin&lt;/a&gt; is &lt;a href="https://www.youtube.com/watch?v=fo6BKY2xR2w"&gt;Obsidian&amp;rsquo;s most popular plugin&lt;/a&gt;, and with good reason.&lt;/p&gt;
&lt;p&gt;It lets me add a calendar on the sidebar:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-calendar.png" alt="Obsidian calendar" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I have the weekly notes option enabled, so clicking on the &lt;code&gt;W&lt;/code&gt; column in the calendar takes me to a note that is automatically created for that week. I use it to keep track of weekly goals and to do a review at the end of the week.&lt;/p&gt;
&lt;p&gt;Clicking on a date in the calendar takes me to the&amp;hellip;&lt;/p&gt;
&lt;h3 id="daily-note"&gt;Daily Note&lt;/h3&gt;
&lt;p&gt;The Daily Notes core plugin comes with Obsidian, but you have to enable it. Once enabled, clicking on a day in the calendar creates/takes you to that day&amp;rsquo;s corresponding note. I use daily notes like a log or workbench for that day, with timestamped headings to help me remember later on what I did at what time. It&amp;rsquo;s a loose way to do time management as well.&lt;/p&gt;
&lt;h2 id="dataview"&gt;Dataview&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/blacksmithgu/obsidian-dataview"&gt;Dataview plugin&lt;/a&gt; is my favorite recent discovery, and it&amp;rsquo;s changing the way I do frontmatter. This plugin brings something akin to SQL (albeit not as powerful) to Obsidian. It lets you query your notes based on some file-related metadata &lt;strong&gt;as well as parameters you set in your frontmatter&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For example, this query:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-dataview" data-lang="dataview"&gt;table file.ctime as Date
from &amp;#34;Meeting&amp;#34;
where file.ctime &amp;gt;= date(today) -dur(1 month)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;when rendered (i.e., viewed in Preview mode in Obsidian), returns a list of all my notes under the subfolder &lt;code&gt;Meeting&lt;/code&gt; in the last month:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/obsidian-dataview-meetings.png" alt="Obsidian Dataview results as a table" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This is huge! I&amp;rsquo;m still updating my notes with the appropriate frontmatter to make more use of this feature, but here are some use cases I can think of that would be handy at work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find all customers with tag &lt;code&gt;annual&lt;/code&gt; that I haven&amp;rsquo;t spoken to in the last year.&lt;/li&gt;
&lt;li&gt;Find a load testing tool with feature &lt;code&gt;Slack_integration = true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find that woman I met at the Automation Test Guild conference by searching for &lt;code&gt;job = tester&lt;/code&gt; and &lt;code&gt;tag = AutomationTestGuild&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Return all notes tagged &lt;code&gt;#plugin_ideas&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that&amp;rsquo;s just the beginning!&lt;/p&gt;
&lt;h2 id="collaboration"&gt;Collaboration&lt;/h2&gt;
&lt;p&gt;Obsidian itself does not (yet) have collaboration features. This is a bummer, but given how quickly they&amp;rsquo;ve delivered on features that the community has clamored for (block references and mobile apps come immediately to mind), I&amp;rsquo;m hopeful that they&amp;rsquo;ll eventually improve in this area.&lt;/p&gt;
&lt;p&gt;In the meantime, I&amp;rsquo;ve still found ways to work on the same vault with other people.&lt;/p&gt;
&lt;p&gt;Remember how any folder of .md files can be opened as an Obsidian vault? Well, I&amp;rsquo;m technically collaborating with other people on that repo for work documentation, even if most people don&amp;rsquo;t use Obsidian. All I had to do was add the &lt;code&gt;.obsidian&lt;/code&gt; directory to the &lt;code&gt;.gitignore&lt;/code&gt; file, and now, nobody really even needs to know I&amp;rsquo;m using Obsidian to view/edit the documentation. They just see me pushing to the repository as if I had used a text editor like VSCode.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also had the experience of collaborating with someone who also used Obsidian. Since we were working closely together on the same document, we opted to put our shared vault on Dropbox. Dropbox&amp;rsquo;s main advantage over the Git solution is that Dropbox let us edit notes and see each other&amp;rsquo;s edits in real time. If you&amp;rsquo;re looking for an experience similar to Google Docs, this way got the closest to that. We did run into some quirks: if the Dropbox vault is also a Git repository, be aware that checking out a different branch will also change the contents of the directory for the other person.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Note-taking and personal management aren&amp;rsquo;t just for students and teachers; I think they&amp;rsquo;re particularly useful in tech, where knowledge work is highly prized. I&amp;rsquo;m talking about Obsidian because that&amp;rsquo;s my current tool of choice, but I think the tool you use is less important than the systems you put in place to help yourself learn and create.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d prefer the video version, here&amp;rsquo;s a video I made showing this process:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/D7e1ud_Dk24?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Presentation Slides as Code</title><link>https://nicolevanderhoeven.com/blog/20210302-presentation-slides-as-code/</link><pubDate>Tue, 02 Mar 2021 19:07:31 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20210302-presentation-slides-as-code/</guid><description>&lt;p&gt;I was working on a presentation for later this month when &lt;a href="https://mariedrake.com"&gt;Marie Drake&lt;/a&gt; told me about &lt;a href="https://slides.com"&gt;slides.com&lt;/a&gt;. It&amp;rsquo;s basically a Powerpoint replacement (which, let&amp;rsquo;s be honest, needs replacing) in that it lets you create slides &lt;em&gt;and&lt;/em&gt; it lets you present from it. It even goes beyond Powerpoint in that it also saves your slides publicly for people to view later.&lt;/p&gt;
&lt;p&gt;Before she&amp;rsquo;d mentioned slides.com, I had been planning on creating a presentation on &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;, my note-taking tool of choice. The advantage of this approach is that presentations in Obsidian are just Markdown files with &lt;code&gt;---&lt;/code&gt; between lines of text to delineate slides.&lt;/p&gt;
&lt;p&gt;The problem with using Obsidian on its own is that it&amp;rsquo;s severely lacking in features, and it&amp;rsquo;s also not shareable. You can, of course, publish the Markdown file, but people won&amp;rsquo;t be able to view them in slide format. After playing with slides.com, however, I also discovered it had its own weaknesses. It is completely UI-driven, so there&amp;rsquo;s no way to create slides just from a Markdown file. It&amp;rsquo;s also hosted by them, so there are limitations imposed, depending on which pricing tier you pay for.&lt;/p&gt;
&lt;p&gt;So I got to Googling. And I think I found a pretty good setup for my needs.&lt;/p&gt;
&lt;h2 id="the-end-result"&gt;The end result&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I write the presentation in Obsidian, in Markdown. &lt;a href="https://gist.github.com/nicolevanderhoeven/752017196efc1ee861c6d3baa5dfca31"&gt;Here&amp;rsquo;s&lt;/a&gt; what that looks like in a sample presentation where I&amp;rsquo;m trying out a bunch of features.&lt;/li&gt;
&lt;li&gt;I use &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; to generate a static website and convert the Markdown files into javascript and html, using &lt;a href="https://github.com/hakimel/reveal.js"&gt;reveal.js&lt;/a&gt; and the &lt;a href="https://github.com/dzello/reveal-hugo"&gt;reveal-hugo&lt;/a&gt; theme.&lt;/li&gt;
&lt;li&gt;I push the static website to a remote repository on &lt;a href="https://github.com"&gt;GitHub&lt;/a&gt;. &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt; serves that site (for free!) on my custom subdomain.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check out the final product here:
&lt;a href="https://slides.nicolevanderhoeven.com/sample"&gt;https://slides.nicolevanderhoeven.com/sample&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210302-reveal-hugo-sample.png" alt="A screenshot of the title presentation slide" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="the-setup"&gt;The setup&lt;/h2&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;An Obsidian vault (or you can actually just use any text editor)&lt;/li&gt;
&lt;li&gt;Git installed and set up with credentials&lt;/li&gt;
&lt;li&gt;A GitHub account&lt;/li&gt;
&lt;li&gt;Hugo installation (&lt;code&gt;brew install hugo&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="create-a-new-hugo-site"&gt;Create a new Hugo site&lt;/h3&gt;
&lt;p&gt;Open up a terminal window and &lt;code&gt;cd&lt;/code&gt; to a folder in your Obsidian vault. Then, create a new Hugo-generated site:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hugo new site slides
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace &lt;code&gt;slides&lt;/code&gt; with the name of your new site.&lt;/p&gt;
&lt;p&gt;You should have a new directory, &lt;code&gt;slides&lt;/code&gt;, in your Obsidian vault.&lt;/p&gt;
&lt;h3 id="add-the-reveal-hugo-theme"&gt;Add the reveal-hugo theme&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cd&lt;/code&gt; into &lt;code&gt;slides&lt;/code&gt; and clone the reveal-hugo repo. The documentation suggests using a submodule:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git submodule add git@github.com:dzello/reveal-hugo.git themes/reveal-hugo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Personally, I just cloned it because I know I&amp;rsquo;m probably going to be messing around with it substantially:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone git@github.com:dzello/reveal-hugo.git themes/reveal-hugo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should have a &lt;code&gt;/themes/reveal-hugo&lt;/code&gt; folder that contains the theme.&lt;/p&gt;
&lt;h3 id="configure-reveal-hugo-theme"&gt;Configure reveal-hugo theme&lt;/h3&gt;
&lt;p&gt;Set up your new Hugo site to use the reveal-hugo theme by adding the following to &lt;code&gt;/config.toml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;theme = &amp;#34;reveal-hugo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;markup.goldmark.renderer]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;unsafe = true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;outputFormats.Reveal]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;baseName = &amp;#34;index&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;mediaType = &amp;#34;text/html&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;isHTML = true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;Reveal&lt;/code&gt; part is what lets you output text in slide format.&lt;/p&gt;
&lt;h3 id="create-your-slides"&gt;Create your slides&lt;/h3&gt;
&lt;p&gt;Go to &lt;code&gt;/content&lt;/code&gt; and create a file there, called &lt;code&gt;_index.md&lt;/code&gt;. The filename is important, as is its location (I learned that the hard way). This file is going to be the first page people see, so it should probably be a list of your presentation slides. For now, put in something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;+++
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;title = &amp;#34;My presentations&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;outputs = [&amp;#34;Reveal&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;+++
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Here are my presentation slides!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#f92672"&gt;sample presentation&lt;/span&gt;](&lt;span style="color:#a6e22e"&gt;/sample&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, create a new folder for your presentation, such as &lt;code&gt;sample&lt;/code&gt;. In &lt;code&gt;/content/sample&lt;/code&gt;, create a file called &lt;code&gt;_index.md&lt;/code&gt;. Again , the filename is also important here (guess how I learned that).&lt;/p&gt;
&lt;p&gt;This index file is going to be where you actually create your slides. Here&amp;rsquo;s the context of my &lt;code&gt;/content/sample/_index.md&lt;/code&gt;:&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;Each slide is separated by &lt;code&gt;---&lt;/code&gt;, just like in Obsidian, which apparently uses the same converter that reveal-hugo does, &lt;a href="https://github.com/hakimel/reveal.js"&gt;reveal.js&lt;/a&gt;. Note that if you want to try out having an image as a background to one of the slides, you&amp;rsquo;ll need to add that to your &lt;code&gt;/sample&lt;/code&gt; folder and change the filename in the slide in this part:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;slide background-image&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;lucrezia-carnelos-AsM4T46tqcU-unsplash.jpg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="generate-your-site"&gt;Generate your site&lt;/h3&gt;
&lt;p&gt;Now it&amp;rsquo;s time to generate your site! Wait - didn&amp;rsquo;t we already do that? Sort of that. That was creating the framework for the site, but now you need to get Hugo to convert those Markdown files you&amp;rsquo;ve just created into something publishable. So cd to the root directory &lt;code&gt;slides&lt;/code&gt; and run this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hugo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That command should generate a &lt;code&gt;slides/public&lt;/code&gt; folder, where all the publishable code will go.&lt;/p&gt;
&lt;p&gt;At this point, you can also check your work by starting a local server with &lt;code&gt;hugo server&lt;/code&gt; and navigating to &lt;code&gt;http://localhost:1313&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="push-your-site-to-a-remote-github-repo"&gt;Push your site to a remote GitHub repo&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cd&lt;/code&gt; into your public folder. This is important, as I initially started the repo in &lt;code&gt;slides&lt;/code&gt; and it took me ages to realize that GitHub Pages requires an &lt;code&gt;index.html&lt;/code&gt; in the root directory to function.&lt;/p&gt;
&lt;p&gt;Initialize a Git repo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Stage and commit your changes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit -m &lt;span style="color:#e6db74"&gt;&amp;#39;Initial commit&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create a repository on your GitHub account called &lt;code&gt;username.github.io&lt;/code&gt; where &lt;code&gt;username&lt;/code&gt; is your GitHub username. (If you already have a repo called that, you can alternatively push the contents of &lt;code&gt;public&lt;/code&gt; to a branch called &lt;code&gt;gh-pages&lt;/code&gt;). The repo name matters.&lt;/p&gt;
&lt;p&gt;Push your changes to your repo.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git push -u origin master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should now have a repository on GitHub called &lt;code&gt;username.github.io&lt;/code&gt;, containing everything in &lt;code&gt;public&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="set-up-github-pages"&gt;Set up GitHub Pages&lt;/h3&gt;
&lt;p&gt;On GitHub, go to the repository settings and scroll down to the GitHub Pages section. Select the right branch (&lt;code&gt;master&lt;/code&gt; in most cases) and click Save.&lt;/p&gt;
&lt;h3 id="optional-using-a-custom-domain"&gt;(Optional) Using a custom domain&lt;/h3&gt;
&lt;p&gt;If you want to use a custom domain, enter it in the repository settings from the previous step.&lt;/p&gt;
&lt;p&gt;Then, go to your domain registrar and add a CNAME record pointing &lt;code&gt;www&lt;/code&gt; to &lt;code&gt;username.github.io&lt;/code&gt;. &lt;a href="https://docs.github.com/en/github/working-with-github-pages/about-custom-domains-and-github-pages"&gt;Here&amp;rsquo;s more information on that&lt;/a&gt; from GitHub.&lt;/p&gt;
&lt;p&gt;It can take a few hours for DNS records to propagate, so after you set this up, take a break before you convince yourself you&amp;rsquo;ve set something up wrong. ;)&lt;/p&gt;
&lt;h3 id="check-out-your-new-site"&gt;Check out your new site!&lt;/h3&gt;
&lt;p&gt;When you navigate to your new site, you should first see the contents of &lt;code&gt;slides/content/_index.md&lt;/code&gt;, which has a link to your sample presentation. Clicking on that should &lt;em&gt;reveal&lt;/em&gt; (ha) the slides one by one. You can navigate through them with the arrow keys, and you can also see speaker notes (if you put any in, which I did on the third slide if you copied my sample slides).&lt;/p&gt;
&lt;h3 id="other-issues-i-faced"&gt;Other issues I faced&lt;/h3&gt;
&lt;p&gt;I ran into a few issues during this setup because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I was already tracking changes in my Obsidian vault with Git&lt;/li&gt;
&lt;li&gt;I already had a repo called &lt;code&gt;nicolevanderhoeven.github.io&lt;/code&gt;, and I needed to create a branch called &lt;code&gt;gh-pages&lt;/code&gt; to still use GitHub Pages.&lt;/li&gt;
&lt;li&gt;My Obsidian repo on GitHub is a private one, but I wanted the slides to be public.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have a similar setup, maybe the following instructions will help.&lt;/p&gt;
&lt;h4 id="use-worktrees-not-subtrees-or-submodules"&gt;Use worktrees, not subtrees or submodules&lt;/h4&gt;
&lt;p&gt;After attempts to make this setup work with submodules and subtrees, both of which were way too complicated for what I needed, I settled on using worktrees.&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;public&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;. From &lt;code&gt;slides&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;public/&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; .gitignore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create a worktree for &lt;code&gt;public&lt;/code&gt;, to be tracked on the branch &lt;code&gt;gh-pages&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git worktree add public gh-pages
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After you generate the static code via &lt;code&gt;hugo&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd public
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit -m &lt;span style="color:#e6db74"&gt;&amp;#39;Commit message&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git push origin gh-pages
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Your &lt;code&gt;gh-pages&lt;/code&gt; branch should contain only the files in &lt;code&gt;public&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="github-pages-with-a-private-repo"&gt;GitHub Pages with a private repo&lt;/h4&gt;
&lt;p&gt;GitHub Pages is free, but if you want to be able to use it with a private repo, &lt;a href="https://docs.github.com/en/github/working-with-github-pages/troubleshooting-custom-domains-and-github-pages"&gt;you can&amp;rsquo;t&lt;/a&gt;. Sorry!&lt;/p&gt;
&lt;p&gt;You can look into using GitHub Actions to copy &lt;code&gt;public&lt;/code&gt; from your private repo to a public one, or you could just &lt;a href="https://github.com/pricing"&gt;spend the $4/month&lt;/a&gt; to remove the restriction. I opted to pay, because I love GitHub anyway.&lt;/p&gt;
&lt;h2 id="presentation-as-code"&gt;Presentation as code&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s it! I ran into a few snags while I was attempting to implement this, so I thought I&amp;rsquo;d document it here in case it helps anyone else. Despite the setbacks, I think it&amp;rsquo;s worth it&amp;ndash; I can now whip up presentations in Markdown within Obsidian and have that published to a site I control for people to look through at their own leisure. This should speed up my presentation creation process considerably.&lt;/p&gt;
&lt;p&gt;Theoretically.&lt;/p&gt;
&lt;p&gt;If only I could stop procrastinating on creating this presentation by setting up a framework for creating presentations&amp;hellip;&lt;/p&gt;</description></item><item><title>Readwise to Obsidian</title><link>https://nicolevanderhoeven.com/blog/20210206-readwise-to-obsidian/</link><pubDate>Sat, 06 Feb 2021 09:55:36 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20210206-readwise-to-obsidian/</guid><description>
&lt;meta property=“og:image” content="/assets/20210206-06-readwise-to-obsidian.png" /&gt;
&lt;meta property=”og:image:width” content=”1200″ /&gt;
&lt;meta property=”og:image:height” content=”628″ /&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-06-readwise-to-obsidian.png" alt="Readwise to Obsidian workflow" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;August 10th, 2021: Readwise just released their own Obsidian plugin, &lt;a href="https://help.readwise.io/article/125-how-does-the-readwise-to-obsidian-export-integration-work"&gt;Readwise Official&lt;/a&gt;, which is available now! I now recommend &lt;a href="https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux/"&gt;this method&lt;/a&gt; over the one outlined in this article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m about 9 months into heavily using &lt;a href="https://roamresearch.com"&gt;Roam Research&lt;/a&gt; as my primary note-taking, task management, capture-everything system, and for the most part, that&amp;rsquo;s gone very well. &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt; is another excellent personal knowledge management (PKM) system, though, and I&amp;rsquo;ve been curious about it for almost as long as I&amp;rsquo;ve been using Roam. I&amp;rsquo;ve had it installed for a while, but I never really &lt;em&gt;seriously&lt;/em&gt; attempted to make the switch. Until now. I&amp;rsquo;ve decided to use Obsidian as my primary PKM and zettelkasten for the next month, to see if it&amp;rsquo;s something I can switch to.&lt;/p&gt;
&lt;p&gt;Why I&amp;rsquo;m considering switching from Roam to Obsidian is another post, probably one I&amp;rsquo;ll write at the end of this experiment, when my thoughts and opinions are more solidified. For now, I want to address one of the things I struggled with when switching over: Readwise integration.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://readwise.io/nicole"&gt;Readwise&lt;/a&gt; is an app that&amp;rsquo;s all about importing notes and highlights you&amp;rsquo;ve made from all over the internet and exporting them all into a single format. Readwise supports far more import sources than I use, but here are the ones I&amp;rsquo;ve got hooked up to it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instapaper (for highlights and notes on online articles)&lt;/li&gt;
&lt;li&gt;Amazon Kindle (for highlights on ebooks)&lt;/li&gt;
&lt;li&gt;Twitter (for interesting tweets)&lt;/li&gt;
&lt;li&gt;(Readwise) Web highlighter (for straight highlights off any webpage)&lt;/li&gt;
&lt;li&gt;Airr (for quotes and notes from audio podcasts)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Readwise collates my activity in all of these input sources and automatically sends them to my Roam Research account. Here&amp;rsquo;s what that looks like when it gets pulled into my Daily Note:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-01-readwiseimportsinroam.png" alt="Tweet, podcast, and book notes pulled into Roam Research via Readwise" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Readwise also pulls metadata into Roam, depending on the source. Here&amp;rsquo;s what the page for the book &amp;ldquo;Ready Player Two&amp;rdquo; looks like in Roam:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-02-readyplayertwo.png" alt="Ready Player Two book notes in Roam, imported via Readwise" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Readwise integration is an &lt;em&gt;essential&lt;/em&gt; feature for me in any PKM, because I can take notes wherever I&amp;rsquo;m consuming my content and just have it automagically appear in my notes, tagged and ready for processing. So it&amp;rsquo;s absolutely something I need to have in my Obsidian workflow.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s where things get tricky, because Readwise doesn&amp;rsquo;t &lt;em&gt;have&lt;/em&gt; an Obsidian integration.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-03-readwise-export-options.png" alt="Readwise doesn’t automatically export to Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Source: &lt;a href="https://readwise.io/export"&gt;Readwise Exports&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The closest thing is the Markdown export, but that&amp;rsquo;s not automatic. You can manually trigger an export, and then copy those exports over into your Obsidian vault, and thne make sure they&amp;rsquo;re in the right format with the right tags. I knew I was never going to do that, so I found a way to automate it.&lt;/p&gt;
&lt;h2 id="readwise2directory"&gt;readwise2directory&lt;/h2&gt;
&lt;p&gt;I discovered that Readwise has an API, and briefly thought about building my own integration. Happily, I found &lt;a href="https://github.com/nicrivard/readwise2directory"&gt;this Python project on GitHub&lt;/a&gt; by &lt;a href="https://github.com/nicrivard"&gt;nicrivard&lt;/a&gt;, who had already done that! Why rebuild the wheel? His solution already did what I needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It connects to Readwise via your Readwise access token&lt;/li&gt;
&lt;li&gt;Checks for new highlights or updated ones&lt;/li&gt;
&lt;li&gt;Exports the new/updated highlights to a local folder of your choosing (presumably your Obsidian vault)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It even pulls in highlight tags from Readwise, if you&amp;rsquo;ve created any (which I haven&amp;rsquo;t).&lt;/p&gt;
&lt;p&gt;I &lt;a href="https://github.com/nicolevanderhoeven/readwise2directory"&gt;forked the project here&lt;/a&gt; to add just a few minor touches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changed date format of imported notes&lt;/li&gt;
&lt;li&gt;Added a section to output the import date to a comment block (an &lt;a href="https://forum.obsidian.md/t/obsidian-release-v0-10-12-insider-build/12295"&gt;Obsidian Insider build feature&lt;/a&gt;) rather than YAML frontmatter, so that the date would actually be linked and backlinked.&lt;/li&gt;
&lt;li&gt;Customized tags and filename for my use&lt;/li&gt;
&lt;li&gt;Sort imports into folders within my Obsidian vault depending on source (&lt;code&gt;Article/&lt;/code&gt;, &lt;code&gt;Tweet/&lt;/code&gt;, and so on).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This works out pretty well. Here&amp;rsquo;s an example of an import from the podcast app &lt;a href="https://airr.io"&gt;Airr&lt;/a&gt; into Obsidian, in Edit mode.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-04-airrtoobsidian.png" alt="readwise2directory import from Airr into Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s placed within my &lt;code&gt;Podcast&lt;/code&gt; folder inside my value, is tagged appropriately, and contains links to the source. Switching to Preview mode shows an imported cover photo and even a playable recorder for the part of the podcast that I snipped on Airr:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-05-airrinobsidian_preview.png" alt="Preview mode of imported Airr quote in Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;So that works out well, except for one thing: firing off the script that does all this is still manual.&lt;/p&gt;
&lt;h2 id="scheduling-the-script"&gt;Scheduling the script&lt;/h2&gt;
&lt;p&gt;Luckily, it&amp;rsquo;s pretty easy to schedule a Python script to run at certain intervals. I&amp;rsquo;m on macOS, so I used a built-in utility called &lt;code&gt;cron&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="give-cron-full-system-access"&gt;Give cron full system access&lt;/h3&gt;
&lt;p&gt;Cron needs to have the right permissions to run scripts on my behalf, so I had to go to System Preferences &amp;gt; Security &amp;amp; Privacy &amp;gt; Privacy &amp;gt; Full Disk Access on my laptop and add &lt;code&gt;/usr/bin/cron&lt;/code&gt; to the &amp;ldquo;allowed&amp;rdquo; list.&lt;/p&gt;
&lt;h3 id="open-crontab"&gt;Open crontab&lt;/h3&gt;
&lt;p&gt;Then, from my terminal, I ran the command &lt;code&gt;env EDITOR=nano crontab -e&lt;/code&gt; to open up the list of cronjobs I had currently.&lt;/p&gt;
&lt;h3 id="set-a-schedule"&gt;Set a schedule&lt;/h3&gt;
&lt;p&gt;I added a line to crontab for the schedule I wanted to create. This line has to be in the format&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* * * * * command
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;where the five asterisks correspond to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;minute&lt;/li&gt;
&lt;li&gt;hour&lt;/li&gt;
&lt;li&gt;day of the month&lt;/li&gt;
&lt;li&gt;month&lt;/li&gt;
&lt;li&gt;day of the week&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my case, I wanted it to run every hour, so here&amp;rsquo;s what my schedule looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; * * * * cd /Users/nic/git/readwise2directory/ &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /Library/Frameworks/Python.framework/Versions/3.9/bin/python3 /Users/nic/git/readwise2directory/readwise-GET.py &amp;gt;&amp;gt; /Users/nic/git/readwise2directory/readwiseGET.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="python-issues"&gt;Python issues&lt;/h4&gt;
&lt;p&gt;I had some trouble getting cron to work until I realized it was because I had multiple versions of Python installed. I ran a &lt;code&gt;which python3&lt;/code&gt; to find the path to the version I wanted to use and added that path to the command. I also had to &lt;code&gt;cd&lt;/code&gt; to the project directory, for some reason.&lt;/p&gt;
&lt;p&gt;While I was troubleshooting, I found it really useful to output to a log so I could see what had gone wrong.&lt;/p&gt;
&lt;p&gt;Once I added the schedule as in the example above, though, it worked a treat!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This solution did require some setup at the beginning, but doesn&amp;rsquo;t need any maintenance once it&amp;rsquo;s going. I&amp;rsquo;m pretty happy with it as it means I can keep taking notes on various places from anywhere, and it&amp;rsquo;ll all be waiting for me in my Obsidian vault whenever I&amp;rsquo;m ready to sit down and process them into my system. Thanks to nicrivard for the repo that made it possible!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This post contains an affiliate link for &lt;a href="https://readwise.io/nicole"&gt;Readwise&lt;/a&gt;. Clicking on that link will give you a two-month trial of Readwise, double the usual amount, and they give me a small percentage if you sign up for a plan. Check out &lt;a href="https://nicolevanderhoeven.com/ethics/"&gt;My Ethics Statement&lt;/a&gt; to see my standards for promotions.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Comparing k6 and JMeter for load testing</title><link>https://nicolevanderhoeven.com/blog/20210129-k6-vs-jmeter/</link><pubDate>Fri, 29 Jan 2021 23:01:18 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20210129-k6-vs-jmeter/</guid><description>
&lt;link rel="canonical" href="https://k6.io/blog/k6-vs-jmeter"&gt;
&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://k6.io/blog/k6-vs-jmeter"&gt;k6.io&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/noZppBruOSY?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;When I &lt;a href="https://www.youtube.com/watch?v=zcYeboT5FYE&amp;amp;list=PLJdv3RhAQXNHsC8OOV3ZjwZvC9tRoAYF2"&gt;joined the k6 team in late November&lt;/a&gt;, the foremost question on my mind was &amp;ldquo;How does this compare to JMeter?&amp;rdquo; You see, I come from a performance testing consulting background, and in the last few years, JMeter has been far and away my tool of choice.&lt;/p&gt;
&lt;p&gt;So what am I doing on the k6 site, talking about how much I love JMeter? Well, firstly, it turns out that the k6 team is pretty open and transparent, especially about potential improvements in k6. Secondly, I want to share my reasons for wanting to explore k6 as a load testing tool&amp;ndash; the same reasons that ultimately convinced me to join the team that builds it.&lt;/p&gt;
&lt;h2 id="what-is-jmeter"&gt;What is JMeter?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://jmeter.apache.org/"&gt;JMeter&lt;/a&gt; is an open source load testing tool built entirely in Java by the &lt;a href="https://www.apache.org/"&gt;Apache Foundation&lt;/a&gt;. It was first released in 1998, and it made waves because of its audacity in taking on more popular but proprietary load testing tools. JMeter took what other companies were charging a lot of money for and published it online for everyone to use&amp;ndash; for free. Although scripts can also be extended using code, majority of the scripting in JMeter is done using the UI. At the time of this writing, the latest version of JMeter is 5.4.&lt;/p&gt;
&lt;h2 id="what-is-k6"&gt;What is k6?&lt;/h2&gt;
&lt;p&gt;k6 is an &lt;a href="https://github.com/loadimpact/k6"&gt;open source&lt;/a&gt; load testing tool released by &lt;a href="https://loadimpact.com"&gt;LoadImpact&lt;/a&gt; in 2017. LoadImpact had previously consisted of the SaaS platform (now &lt;a href="https://k6.io/cloud"&gt;k6 Cloud&lt;/a&gt;) and professional services. k6 is written in Go, but scripts are written in Javascript. Taking the opposite tack to JMeter, k6&amp;rsquo;s main selling point is its code-based scripting and heavy prioritization of the developer experience. The latest version of k6 is 0.30.&lt;/p&gt;
&lt;h2 id="which-is-better"&gt;Which is better?&lt;/h2&gt;
&lt;p&gt;This article isn&amp;rsquo;t about which tool is &lt;em&gt;better&lt;/em&gt;, because as much as we might prefer a clear-cut answer, there&amp;rsquo;s no &amp;ldquo;best tool&amp;rdquo;. Everything depends on the situation your team is in: what, how, why, and when you&amp;rsquo;re testing.&lt;/p&gt;
&lt;p&gt;The question that I want to answer here is: &lt;em&gt;What situations does each tool excel in?&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="when-does-jmeter-excel"&gt;When does JMeter excel?&lt;/h1&gt;
&lt;h2 id="when-you-want-to-get-started-quickly"&gt;When you want to get started quickly&lt;/h2&gt;
&lt;h3 id="jmeter-has-a-gui"&gt;JMeter has a GUI&lt;/h3&gt;
&lt;p&gt;Many testing teams make decisions on load testing tools based on this fact alone. Sometimes, if you&amp;rsquo;re in a situation where people haven&amp;rsquo;t done load testing before and need to learn a new tool, a GUI-driven tool like JMeter is simply the most frictionless option. A tool with a GUI will be less daunting for many testers that are used to the mostly no-code UIs of the likes of Postman or SOAPUI.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/01-jmetergui.png" alt="JMeter’s starting screen: a fresh new test plan" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always thought of JMeter as user-friendly, but a developer friend recently pointed out that the Test Plan screen that greets you when you first start JMeter doesn&amp;rsquo;t provide any indication of how to create an HTTP sampler. None of the icons add elements to the test plan. It takes a bit of exploring to realize that right clicking on the test plan, or going to Edit &amp;gt; Add, will show the &amp;ldquo;Add&amp;rdquo; menu that you need. He has a point there: UI is subjective to a certain degree. However, I&amp;rsquo;d argue that it&amp;rsquo;s definitely easier for non-developers to explore a UI than a bit of code.&lt;/p&gt;
&lt;p&gt;k6 doesn&amp;rsquo;t have a GUI packaged within the tool, but the &lt;a href="https://k6.io/docs/test-authoring/test-builder"&gt;k6 Test Builder&lt;/a&gt; is available for free. It&amp;rsquo;s a way to create tests with a GUI interface, and despite its inclusion in k6 cloud, it doesn&amp;rsquo;t require any subscription to use. However, it isn&amp;rsquo;t as fully-featured as JMeter.&lt;/p&gt;
&lt;h3 id="jmeter-supports-many-protocols-and-features-out-of-the-box"&gt;JMeter supports many protocols and features out of the box&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s &lt;a href="https://jmeter.apache.org/index.html"&gt;the official list&lt;/a&gt; of the protocols that JMeter supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web - HTTP, HTTPS (Java, NodeJS, PHP, ASP.NET, …)&lt;/li&gt;
&lt;li&gt;SOAP / REST Webservices&lt;/li&gt;
&lt;li&gt;FTP&lt;/li&gt;
&lt;li&gt;Database via JDBC&lt;/li&gt;
&lt;li&gt;LDAP&lt;/li&gt;
&lt;li&gt;Message-oriented middleware (MOM) via JMS&lt;/li&gt;
&lt;li&gt;Mail - SMTP(S), POP3(S) and IMAP(S)&lt;/li&gt;
&lt;li&gt;Native commands or shell scripts&lt;/li&gt;
&lt;li&gt;TCP&lt;/li&gt;
&lt;li&gt;Java Objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In contrast, k6 supports the following protocols:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web - HTTP/1.1, &lt;a href="https://k6.io/docs/using-k6/protocols/http-2"&gt;HTTP/2&lt;/a&gt; (Java, NodeJS, PHP, ASP.NET, …)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://k6.io/docs/using-k6/protocols/websockets"&gt;WebSockets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://k6.io/docs/using-k6/protocols/grpc"&gt;gRPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SOAP / REST Webservices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The protocols above are those that are natively supported. However, both tools allow the development of third-party plugins, adding support for more protocols. The number of &lt;a href="https://github.com/topics/xk6"&gt;k6 community extensions&lt;/a&gt;, through &lt;a href="https://k6.io/blog/extending-k6-with-xk6"&gt;xk6&lt;/a&gt;, is growing, but &lt;a href="https://jmeter-plugins.org/"&gt;JMeter plugins&lt;/a&gt; still outnumber them. In many cases, the protocols your application uses may make the decision for you of which tool to use.&lt;/p&gt;
&lt;p&gt;JMeter also has most of the features you&amp;rsquo;ll need for a basic load test, and you don&amp;rsquo;t have to code any of it up yourself. Its parent-child element structure means that you can modify a specific element, like an HTTP request, or modify all HTTP requests, with equal ease. Adding think time to all requests is as easy as adding a Uniform Random Timer to your thread group, and all requests within that group will inherit it.
With code-based load testing tools, it can be a bit more difficult to find out what&amp;rsquo;s possible without a UI. Some of the gap, however, can be bridged by an IDE with good autocompletion.&lt;/p&gt;
&lt;h2 id="when-you-want-a-mature-community-with-tons-of-documentation"&gt;When you want a mature community with tons of documentation&lt;/h2&gt;
&lt;p&gt;JMeter&amp;rsquo;s old, and sometimes that&amp;rsquo;s a good thing. It&amp;rsquo;s been around since 1998, so at this point, it&amp;rsquo;s had 22 years to improve and to build a following. JMeter has a presence on nearly every community networking site, and no matter how specific your use case for JMeter is, the chances are that it&amp;rsquo;s been done before. Searching &amp;ldquo;how to load test X with JMeter&amp;rdquo; is bound to yield thousands of hits and some videos showing you exactly how to do it.&lt;/p&gt;
&lt;p&gt;JMeter&amp;rsquo;s &lt;a href="https://jmeter.apache.org/usermanual/component_reference.html"&gt;Component Reference&lt;/a&gt; is proof of JMeter&amp;rsquo;s extensive and thorough documentation. Every element, function, and property is discussed in more detail than most people could ever want, and that&amp;rsquo;s just the &amp;ldquo;official&amp;rdquo; documentation that&amp;rsquo;s on the Apache site. There are thousands of JMeter books, tutorials, and courses by its passionate users.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/apache/jmeter"&gt;JMeter GitHub repository&lt;/a&gt; has 17,290 commits as of December 2020 - evidence of a community of developers that have committed some time and experience to JMeter over the last two decades.&lt;/p&gt;
&lt;p&gt;In comparison, the k6 community is growing, but still small. There is an official and &lt;a href="https://community.k6.io/"&gt;central community forum&lt;/a&gt; that allows users to share their experiences with k6, which is something missing from the JMeter community. The &lt;a href="https://github.com/loadimpact/k6"&gt;k6 repository&lt;/a&gt; does have more stars, but significantly fewer commits at 3,795. Much of the disparity, of course, is because k6 is very new - the open-source tool k6 was only released in 2017.&lt;/p&gt;
&lt;h2 id="when-you-need-a-cost-effective-way-to-do-distributed-load-testing"&gt;When you need a cost-effective way to do distributed load testing&lt;/h2&gt;
&lt;p&gt;One of JMeter&amp;rsquo;s absolute best features is that it gives you a framework for running distributed load tests with it. This is pretty special for a free and open-source tool.&lt;/p&gt;
&lt;p&gt;Distributed testing means ramping up the amount of load you&amp;rsquo;re generating with your load tests, usually by increasing the number of virtual users and running multiple instances of your script on other load generators. JMeter accomplishes this by designating a controller node and letting you set up worker nodes. Each worker node needs to have a copy of &lt;code&gt;jmeter-server&lt;/code&gt;, a utility included with every JMeter installation (in &lt;code&gt;jmeter/bin&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/02-jmeterdistributed.svg" alt="JMeter comes with a native distributed testing mode" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Source: &lt;a href="https://jmeter.apache.org/usermanual/jmeter_distributed_testing_step_by_step.html"&gt;Apache JMeter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These worker nodes then run copies of your test plan and exert load against your application server. You can use on-premise or cloud machines as your worker nodes. A distributed execution like this takes a bit to set up, but it does work surprisingly well.&lt;/p&gt;
&lt;p&gt;k6 doesn&amp;rsquo;t have a &lt;em&gt;native&lt;/em&gt; distributed load testing mode. You &lt;em&gt;can&lt;/em&gt; containerize it yourself and deploy it via Kubernetes, for instance, but it does require a more involved setup. The team is also working on the &lt;a href="https://github.com/k6io/operator"&gt;k6 Operator&lt;/a&gt;, which makes setup significantly easier, but it is experimental and comes with drawbacks such as not supporting threshold synchronization between instances. For now, the easiest way to scale up on k6 is by using the paid service k6 Cloud, which reduces setup time and complexity significantly.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s important to note that JMeter can be quite resource-hungry, and thus is more likely to require multiple load generators to generate load than k6. Depending on the resources of the load generator, JMeter can run about a thousand virtual users on average, and scaling up your test beyond that point will require a distributed execution setup. A single instance of k6, on the other hand, can run tens of thousands of virtual users given the same resources. More on that later.&lt;/p&gt;
&lt;h2 id="when-you-want-canned-reports-built-into-your-load-testing-tool"&gt;When you want canned reports built into your load testing tool&lt;/h2&gt;
&lt;p&gt;JMeter&amp;rsquo;s many listeners allow you to decide exactly what format you want to view results in. Debugging? Use the View Results Tree. Wanting metrics for your load test? Use the Summary or Aggregate Report.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/03-jmeterlistener.png" alt="Adding listeners like View Results Tree in JMeter lets you visualize results in different ways" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;In addition, JMeter can generate HTML reports based on your load tests. They&amp;rsquo;re currently quite limited, but functional, and they have several default graphs to help you analyze your load test. They are not interactive, but they are pretty easy to generate and a good starting point for putting together a report template for your tests.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/04-jmeterhtmlreport.png" alt="JMeter’s built-in HTML dashboard is limited but functional" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;k6 does not have built-in reporting. Its modular nature lends itself better to integrating with &lt;a href="https://k6.io/docs/results-visualization"&gt;many other data analytics tools&lt;/a&gt;, including Grafana, Datadog, New Relic, Amazon CloudWatch, and k6 Cloud&amp;ndash; all of which are better analysis tools and can provide more insights than what JMeter&amp;rsquo;s reports can provide, but they do require you to set up another tool to analyze your tests. If you&amp;rsquo;re running low on time and can make do with its reports, JMeter does really well at providing that extra value out of the box.&lt;/p&gt;
&lt;h1 id="when-does-k6-excel"&gt;When does k6 excel?&lt;/h1&gt;
&lt;h2 id="when-you-want-to-get-started-quickly-1"&gt;When you want to get started quickly&lt;/h2&gt;
&lt;p&gt;Yup, I put this one in both the JMeter and k6 sections. JMeter and k6 can each be easy to get started with, but for very different reasons.&lt;/p&gt;
&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;
&lt;p&gt;k6 installs extremely quickly - on my first installation of it on macOS, it took &lt;a href="https://www.youtube.com/watch?v=y5tteMKZUqk&amp;amp;t=99"&gt;7 seconds&lt;/a&gt; via &lt;code&gt;brew install k6&lt;/code&gt;. It doesn&amp;rsquo;t require nodeJS or any other dependencies, either.&lt;/p&gt;
&lt;p&gt;JMeter itself is fairly easy to install, but before you install JMeter, you need to install Java. Especially if you&amp;rsquo;re using Windows, it&amp;rsquo;s a bit of a rite of passage for a new JMeter user to run into issues adding the environment variables needed for Java to function.&lt;/p&gt;
&lt;p&gt;It can also be confusing to determine which version of Java is the right one. There&amp;rsquo;s JDK, JRE, and recently Oracle has added to the confusion by announcing that some versions of Java (but not all) are going to require a license.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/05-javalicense.png" alt="Oracle’s changing Java licensing can cause confusion for JMeter users" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Source: &lt;a href="https://java.com/en/download/"&gt;Oracle Java&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While JMeter is still free via OpenJDK, I&amp;rsquo;ve spoken to a few testers who mistakenly thought they&amp;rsquo;d have to pay for JMeter. It&amp;rsquo;s a little disconcerting to see Oracle flexing its muscles here, especially if you&amp;rsquo;ve built a whole performance testing suite on Java through JMeter.&lt;/p&gt;
&lt;h3 id="plugins"&gt;Plugins&lt;/h3&gt;
&lt;p&gt;k6 &lt;a href="https://k6.io/blog/extending-k6-with-xk6"&gt;supports plugins&lt;/a&gt;, but they&amp;rsquo;re not necessary. For most use cases, k6&amp;rsquo;s built-in functionality should be more than sufficient. This is in sharp contrast to JMeter, where everything is a plugin &amp;hellip; even the &lt;a href="https://jmeter-plugins.org/wiki/PluginsManager/"&gt;Plugin Manager&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Most JMeter users would argue that there is a standard set of plugins that you should download before you even begin to use it. A new JMeter user might not necessarily know where to find these and may have a less-than-satisfying experience out of the box.&lt;/p&gt;
&lt;p&gt;Here are a few things you &lt;em&gt;won&amp;rsquo;t&lt;/em&gt; have if you stick to a vanilla, no-plugin JMeter installation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Thread groups: No custom load profile, stages, rendezvous&lt;/li&gt;
&lt;li&gt;Controllers: No parallel controller to execute requests in parallel&lt;/li&gt;
&lt;li&gt;Samplers: No HTTP/2 support&lt;/li&gt;
&lt;li&gt;Test Data: No randomization of lines within a CSV file (sequential only)&lt;/li&gt;
&lt;li&gt;Timers: No throughput shaping timer for setting throughput in stages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These functions are all available within k6 from the get-go.&lt;/p&gt;
&lt;h3 id="scripting"&gt;Scripting&lt;/h3&gt;
&lt;p&gt;k6 scripts are written in javascript, making them simple to write and run. All you need is one installer (on Microsoft Windows) or a single command on your terminal and any text editor. For JMeter, you&amp;rsquo;ll need to install a few things locally before you can get started: Java, JMeter itself, and the standard set of plugins you&amp;rsquo;ll need. This can be confusing for new users. Who hasn&amp;rsquo;t struggled with environment variables at some point when learning to use JMeter, especially on Windows?&lt;/p&gt;
&lt;p&gt;It also takes time to learn any new UI, whereas javascript is near-ubiquitous and it&amp;rsquo;s a good language to learn when you&amp;rsquo;re load testing web sites anyway.&lt;/p&gt;
&lt;h2 id="when-you-want-to-maximize-performance-and-efficiency"&gt;When you want to maximize performance and efficiency&lt;/h2&gt;
&lt;h3 id="test-your-test-tools"&gt;Test your test tools!&lt;/h3&gt;
&lt;p&gt;Why does all this matter?&lt;/p&gt;
&lt;p&gt;When we&amp;rsquo;re talking about simulating many virtual users, slow performance = high cost. There is a cost to the number of load generators you use, whether it&amp;rsquo;s the cost to acquire and maintain them (on-prem) or the cost of a cloud service.&lt;/p&gt;
&lt;p&gt;It makes sense to consider the performance limitations of your load testing tool before you use it to improve your own application&amp;rsquo;s performance.&lt;/p&gt;
&lt;h3 id="k6-builds-on-gos-stellar-performance"&gt;k6 builds on Go&amp;rsquo;s stellar performance&lt;/h3&gt;
&lt;p&gt;k6 is written in Go, and Go is built with performance in mind. &amp;ldquo;Go is a compiled language and does not need to be interpreted, unlike Java or Python. There is no added layer of complexity.&amp;rdquo; It also has no external dependencies, which is a good thing. Fewer moving parts for a performance tester means fewer potential sources of a bottleneck.&lt;/p&gt;
&lt;p&gt;The simplest and most common way for a load testing tool to run a virtual user is to assign one virtual user to one kernel or OS thread. However, the 1 Thread: 1 Virtual User paradigm has a serious flaw: when a virtual user is waiting for a response or executing a programmed &lt;code&gt;sleep()&lt;/code&gt;, the thread is also blocked, and cannot process other work.&lt;/p&gt;
&lt;p&gt;In k6, each virtual user is run on a goroutine, not a thread. What difference does that make? Well, goroutines can be controlled by something called the Go Scheduler, which acts like a traffic cop. It reuses idle threads and intelligently assigns work, by allowing &amp;ldquo;work stealing&amp;rdquo; and work hand-offs between threads. Does this sound familiar? This is the same principle that load balancers are built on: An external monitor that oversees the flow of work improves general performance. Go itself is intrinsically load-balanced in a way that many programming languages aren&amp;rsquo;t, which makes it the perfect foundation for a load testing tool.&lt;/p&gt;
&lt;p&gt;Being able to leverage Go&amp;rsquo;s innate performance optimizations also translates into significantly less memory utilization. One thread running k6 doesn&amp;rsquo;t go above 100 kb, whereas a JVM thread like JMeter uses, for instance, &lt;a href="https://www.baeldung.com/jvm-configure-stack-sizes"&gt;uses the default of 1MB&lt;/a&gt;. That&amp;rsquo;s 1000% more than k6! Of course, Java allows a user to tweak an app&amp;rsquo;s memory utilization, so the difference is usually not quite as stark, but it&amp;rsquo;s still interesting to note that Go has a much lower starting point.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip; creating [a] new thread in Java is not memory efficient. As every thread consumes approx 1MB of the memory heap size and eventually if you start spinning thousands of threads, they will put tremendous pressure on the heap and will cause shut down due to out of memory. Also, if you want to communicate between two or more threads, it’s very difficult. - &lt;a href="https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65"&gt;Keval Patel&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="performance-benefits-in-practice"&gt;Performance benefits in practice&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Fewer load generators needed&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;k6&amp;rsquo;s comparatively better performance means it needs fewer load generators to execute a given amount of load. Rafaela Azevedo made a comparison of the memory used by k6 and JMeter, and &lt;a href="https://azevedorafaela.com/2020/07/06/load-tests-jmeter-vs-k6/"&gt;here are her results&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;JMeter took up 760 MB of memory.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/06-jmeterbenchmark.png" alt="In an independent test by Rafaela Azevedo, JMeter took up 760 MB of memory" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Source: &lt;a href="https://azevedorafaela.com/2020/07/06/load-tests-jmeter-vs-k6/"&gt;Rafaela Azevedo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Meanwhile, k6 took up 256 MB of memory.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/07-k6benchmark.png" alt="In an independent test by Rafaela Azevedo, k6 took up 256 MB of memory compared to JMeter’s 760 MB for the same test" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Source: &lt;a href="https://azevedorafaela.com/2020/07/06/load-tests-jmeter-vs-k6/"&gt;Rafaela Azevedo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Rafaela&amp;rsquo;s findings are further affirmed by our own benchmarks.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/08-internalbenchmarks.png" alt="A comparison of maximum throughput and memory for the most popular open-source load testing tools" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Source: Ragnar Lönn, in our &lt;a href="https://k6.io/blog/comparing-best-open-source-load-testing-tools"&gt;Load Testing Tool Comparison&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;k6&amp;rsquo;s lower memory footprint is the reason it can run more virtual users and generate more load than average. Whether you&amp;rsquo;re using load generators that are on-premise or in a cloud, you&amp;rsquo;ll pay less for provisioning costs when using k6. This cost saving makes k6 a great tool for budget-conscious teams.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The common misconception of many load testers is that distributed execution (ability to launch a load test on multiple machines) is required to generate large load. This is not the case with k6. - &lt;a href="https://azevedorafaela.com/2020/07/06/load-tests-jmeter-vs-k6/"&gt;Rafaela Azevedo&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;k6 is different from many other load testing tools in the way it handles hardware resources. A single k6 process will efficiently use all CPU cores on a load generator machine. A single instance of k6 is often enough to generate load of 30.000-40.000 simultaneous users (VUs). This amount of VUs can generate upwards of 300,000 requests per second (RPS).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Unless you need more than 100,000-300,000 requests per second (6-12M requests per minute), a single instance of k6 will likely be sufficient for your needs. - &lt;a href="https://k6.io/docs/testing-guides/running-large-tests"&gt;k6 documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;No out of memory errors&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;k6 is also good for when you&amp;rsquo;re short on time. Using JMeter means getting familiar with how to performance tune Java and how to fix the &lt;a href="https://www.eginnovations.com/blog/top-10-java-performance-problems/"&gt;most common Java performance problems&lt;/a&gt;, because there are several. The one that I&amp;rsquo;ve personally run into the most while using JMeter is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ERROR - jmeter.threads.JMeterThread: Test failed! java.lang.OutOfMemoryError:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Java heap space.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;re not really a JMeter load tester until you&amp;rsquo;ve seen this error. :)&lt;/p&gt;
&lt;p&gt;The cause for this error is usually that there was an insufficient amount of Heap memory allocated to JMeter. Yup&amp;ndash; in addition to monitoring your load generator&amp;rsquo;s memory usage, you&amp;rsquo;ll also need to monitor the JVM&amp;rsquo;s memory usage. You can set the amount of heap memory allocated to JMeter by modifying the JMeter binary itself and changing this line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; This is the base heap size &lt;span style="color:#f92672"&gt;--&lt;/span&gt; you may increase or decrease it &lt;span style="color:#66d9ef"&gt;to&lt;/span&gt; fit your
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; system&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;s memory availability:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;${HEAP:=&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;Xms1g &lt;span style="color:#f92672"&gt;-&lt;/span&gt;Xmx1g &lt;span style="color:#f92672"&gt;-&lt;/span&gt;XX:MaxMetaspaceSize&lt;span style="color:#f92672"&gt;=&lt;/span&gt;256m&lt;span style="color:#e6db74"&gt;&amp;#34;}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Even JMeter official documentation suggests that you increase the Java Heap size beyond 1GB before you even run your first load test.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Increase the Java Heap size. By default JMeter runs with a heap of 1 GB, this might not be enough for your test and depends on your test plan and number of threads you want to run - &lt;a href="https://jmeter.apache.org/usermanual/get-started.html"&gt;Apache JMeter&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;No GUI means no resource overhead during load tests&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GUIs typically add significantly more overhead to an application, which is one of the reasons why k6 doesn&amp;rsquo;t have one.&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re running a load test, the best practice is to run it from the command line anyway&amp;ndash; because otherwise, it interferes with your results. Code-based load testing tools are performant from the beginning.&lt;/p&gt;
&lt;p&gt;JMeter is peppered with warnings about the GUI&amp;rsquo;s effect on performance. Here&amp;rsquo;s one from the &amp;ldquo;JMeter documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&amp;rsquo;t run load test using GUI mode ! - &lt;a href="https://jmeter.apache.org/usermanual/get-started.html"&gt;Apache JMeter&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And here&amp;rsquo;s a message that occurs upon startup of JMeter:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/09-jmeterguimodewarning.png" alt="JMeter’s startup screen warns users not to use the GUI mode for load testing" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Not having a GUI means there&amp;rsquo;s one less gotcha. Any load testing scripts you create in k6 are ready for prime-time execution whenever you&amp;rsquo;re ready.&lt;/p&gt;
&lt;h2 id="when-you-want-to-do-goal-oriented-testing"&gt;When you want to do goal-oriented testing&lt;/h2&gt;
&lt;p&gt;The most important question a load tester can ask when starting a new load testing project is &amp;ldquo;Why&amp;rdquo;?
Test plans should be made to directly address a team&amp;rsquo;s reasons for wanting to do load testing, and that should be reflected in the non-functional requirements. Non-functional requirements, in turn, should then give some guidance as to the bounds of acceptable performance. A common example for these bounds, or thresholds, is an average response time of less than 3 seconds for all transactions during the test.
k6 does this natively with &lt;a href="https://k6.io/docs/using-k6/thresholds"&gt;global thresholds&lt;/a&gt; worked into the script, and you can also create your own metric to use in a threshold:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6/http&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;Rate&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6/metrics&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;myFailRate&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Rate&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;failed requests&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;thresholds&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;failed requests&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;rate&amp;lt;0.1&amp;#39;&lt;/span&gt;], &lt;span style="color:#75715e"&gt;// threshold on a custom metric
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;http_req_duration&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;p(95)&amp;lt;500&amp;#39;&lt;/span&gt;], &lt;span style="color:#75715e"&gt;// threshold on a standard metric
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;https://test-api.k6.io/public/crocodiles/1/&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;myFailRate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;!==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Source: &lt;a href="https://k6.io/docs/using-k6/thresholds"&gt;k6 documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;JMeter doesn&amp;rsquo;t natively support thresholds at the test-level. There are a few ways that I&amp;rsquo;ve been able to work around this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Duration Assertion&lt;/strong&gt;: You can add a Duration Assertion to any sampler in JMeter, which allows you set an acceptable response time for that request in milliseconds, beyond which the request (and the transaction that encompasses it) would be marked as failed. However, this is still just at the individual request level, and even putting the duration assertion element at the Test Plan level only applies the same threshold to each child request. It doesn&amp;rsquo;t constitute pass/fail criteria for the test as a whole.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Timers&lt;/strong&gt;: For criteria related to throughput, you can use the Constant Throughput Timer or the Throughput Shaping Timer (plugin). This approach controls how many requests per second are spent. In my experience, it&amp;rsquo;s worth testing how these timers behave when using nested transaction controllers - it may take some rearranging to get right.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Plugin&lt;/strong&gt;: When integrating JMeter with Jenkins, you can use the Performance Plugin to set error thresholds and mark test runs (&amp;ldquo;builds&amp;rdquo;) as unstable or failed within Jenkins.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom Code&lt;/strong&gt;: You can use the JSR223 Sampler to write a bit of Groovy or BeanShell code that collects and parses response metrics.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem is that even though these options make setting thresholds possible in JMeter, they&amp;rsquo;re still workarounds that don&amp;rsquo;t come with JMeter out of the box or don&amp;rsquo;t adequately address the creation of different types of thresholds (error, response time, throughput, CPU, memory). What I end up doing, more often than not, is exporting the raw data from JMeter and doing the analysis myself using another tool.&lt;/p&gt;
&lt;h2 id="when-youre-part-of-a-team-of-scripters"&gt;When you&amp;rsquo;re part of a team of scripters&lt;/h2&gt;
&lt;p&gt;k6 shines in situations where more than one person could be doing the scripting.&lt;/p&gt;
&lt;h3 id="it-strikes-a-middle-ground-that-developers-and-testers-can-compromise-on"&gt;It strikes a middle ground that developers and testers can compromise on&lt;/h3&gt;
&lt;p&gt;Software quality should not be the sole responsibility of testers; quality should be baked into every activity when building software. One of the challenges in implementing this concept, however, is that there has traditionally been a segmentation in tools and languages used by team members with different functions. Developers use one tool; testers use another. It&amp;rsquo;s difficult to encourage developers to test and testers to code in this environment, and many testing tools only further encourage this divide. A developer can&amp;rsquo;t use a tool like JMeter for anything but testing, so the incentive to invest the effort in learning it is lower.&lt;/p&gt;
&lt;p&gt;k6 bridges the gap by bringing testing concepts and features to an environment and language that developers are already spending their time in. Javascript is useful for more than just writing load testing scripts, and k6 works well with any IDE or text editor. A frictionless developer experience is central to the k6 philosophy.&lt;/p&gt;
&lt;h3 id="its-easier-to-collaborate-on-scripts"&gt;It&amp;rsquo;s easier to collaborate on scripts&lt;/h3&gt;
&lt;p&gt;Once you&amp;rsquo;ve convinced both devs and testers to contribute to load testing scripts, how do you keep track of changes? How do you manage scripts that multiple people are working on at the same time?
With tests as code, the easiest way is to just use versioning tools like Git, the way you probably are already using for your application code. Again, no new framework or processes to learn here.
k6&amp;rsquo;s conciseness and portability really proves to be an advantage in collaboration situations. To illustrate this, let&amp;rsquo;s consider a simple test script that contains a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A GET request to &lt;a href="https://test.k6.io"&gt;our test site&lt;/a&gt;, &lt;code&gt;https://test.k6.io&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A transaction that calls this request &lt;code&gt;01_Home&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A check to fail non-HTTP 200 responses or responses that don&amp;rsquo;t have the text &lt;code&gt;Collection of simple web-pages suitable for load testing&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Variable think time&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;ll just run that as a single user, for a single iteration. Here&amp;rsquo;s what that script looks like in k6:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6/http&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;check&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;k6&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;https://test.k6.io&amp;#39;&lt;/span&gt;, {&lt;span style="color:#a6e22e"&gt;tags&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;01_Home&amp;#39;&lt;/span&gt; }});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;check&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;is status 200&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;text verification&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;includes&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Collection of simple web-pages suitable for load testing&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;sleep&lt;/span&gt;(Math.&lt;span style="color:#a6e22e"&gt;random&lt;/span&gt;() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can track JMeter scripts on Git, too. Here&amp;rsquo;s what the same script looks like in JMeter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;jmeterTestPlan&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;1.2&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;properties=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;jmeter=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5.3&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;TestPlan&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlanGui&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlan&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Test Plan&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlan.comments&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlan.functional_mode&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlan.tearDown_on_shutdown&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;true&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlan.serialize_threadgroups&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;elementProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlan.user_defined_variables&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;elementType=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Arguments&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ArgumentsPanel&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Arguments&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;User Defined Variables&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;collectionProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Arguments.arguments&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/elementProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TestPlan.user_define_classpath&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/TestPlan&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;ThreadGroup&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroupGui&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Thread Group&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.on_sample_error&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;continue&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;elementProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.main_controller&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;elementType=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;LoopController&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;LoopControlPanel&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;LoopController&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Loop Controller&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;LoopController.continue_forever&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;LoopController.loops&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;1&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/elementProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.num_threads&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;1&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.ramp_time&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;1&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.scheduler&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.duration&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.delay&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ThreadGroup.same_user_on_next_iteration&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;true&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/ThreadGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;TransactionController&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TransactionControllerGui&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TransactionController&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;01_Home&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TransactionController.includeTimers&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;TransactionController.parent&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;true&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/TransactionController&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;HTTPSamplerProxy&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HttpTestSampleGui&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSamplerProxy&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTP Request&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;elementProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPsampler.Arguments&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;elementType=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Arguments&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPArgumentsPanel&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Arguments&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;User Defined Variables&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;collectionProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Arguments.arguments&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/elementProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.domain&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;test.k6.io&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.port&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.protocol&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;https&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.contentEncoding&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.path&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.method&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;GET&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.follow_redirects&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;true&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.auto_redirects&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.use_keepalive&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;true&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.DO_MULTIPART_POST&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.embedded_url_re&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.connect_timeout&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;HTTPSampler.response_timeout&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/HTTPSamplerProxy&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;ResponseAssertion&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;AssertionGui&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ResponseAssertion&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Response Assertion&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;collectionProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Asserion.test_strings&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;-745788246&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;Collection of simple web-pages suitable for load testing&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/collectionProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Assertion.custom_message&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Assertion.test_field&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;Assertion.response_data&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;boolProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Assertion.assume_success&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;lt;/boolProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;intProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Assertion.test_type&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;16&lt;span style="color:#f92672"&gt;&amp;lt;/intProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/ResponseAssertion&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;hashTree/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;UniformRandomTimer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;guiclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;UniformRandomTimerGui&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testclass=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;UniformRandomTimer&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testname=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Uniform Random Timer&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enabled=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ConstantTimer.delay&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;1000&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;stringProp&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;RandomTimer.range&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;4000&lt;span style="color:#f92672"&gt;&amp;lt;/stringProp&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/UniformRandomTimer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;hashTree/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/hashTree&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/jmeterTestPlan&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which one is easier to understand? If you had to modify the script to add a request, which format would you choose to do it in?&lt;/p&gt;
&lt;p&gt;Scripting in XML makes merge conflicts more likely and breaking changes in automation pipelines way more difficult to pinpoint.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve collaborated with others on JMeter scripts in the past, and we always opened up scripts in the GUI. It&amp;rsquo;s way easier to spot changes there - but that also created some new issues.&lt;/p&gt;
&lt;h2 id="when-you-want-something-easy-to-maintain"&gt;When you want something easy to maintain&lt;/h2&gt;
&lt;h3 id="versioning-for-load-testing"&gt;Versioning for load testing&lt;/h3&gt;
&lt;p&gt;When ramping up load tests to more than one load generator, keeping track of the versions of necessary components ensures that one load generator&amp;rsquo;s traffic is the same as another&amp;rsquo;s. The value of reducing variables in scientific experiments holds true for load testing.&lt;/p&gt;
&lt;p&gt;With k6, you just need to worry about the version of k6 and the script. No other dependencies are required. If you&amp;rsquo;re using plugins, you&amp;rsquo;d need to track those too, but as mentioned previously, plugins in k6 aren&amp;rsquo;t as necessary as they are in some other tools &amp;ndash; and k6 is available as a Docker image to reduce those issues.&lt;/p&gt;
&lt;p&gt;With something like JMeter, there are more levels to consider. There&amp;rsquo;s the version of JMeter itself, Java, versions of plugins, and then the script. Every new layer introduces more changes for incompatibilities. In previous projets I&amp;rsquo;ve worked on, someone would inevitably try out a new plugin and forget to discuss it with the team, and anyone who tried to open that script in their copy of JMeter would see this error:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/10-jmeterpluginerror.png" alt="This is the error that JMeter throws when you attempt to open a test plan that includes a plugin you don’t have installed locally" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The error means there&amp;rsquo;s a plugin that the script uses that JMeter doesn&amp;rsquo;t recognize, but depending on the plugin, it&amp;rsquo;s not always this easy to pinpoint which one (it was the &lt;code&gt;Ultimate Thread Group&lt;/code&gt; in this case).
You don&amp;rsquo;t want that happening in the middle of your load test because you forgot to add a plugin to some of your load generators!&lt;/p&gt;
&lt;h3 id="its-easier-to-link-test-code-with-application-code"&gt;It&amp;rsquo;s easier to link test code with application code&lt;/h3&gt;
&lt;p&gt;If your application code is in Javascript, k6 is a shoo-in as a load testing tool. You could build tests that are tightly integrated with application code by importing objects and having your tests interacting with them directly. This approach would reduce a lot of time spent in refactoring test scripts when objects are changed.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re already using VS Code, k6 even has a &lt;a href="https://marketplace.visualstudio.com/items?itemName=k6.k6"&gt;VS Code plugin&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="test-as-code-gives-you-control"&gt;Test as code gives you control&lt;/h3&gt;
&lt;p&gt;Having your scripts be pure code reduces ambiguities in that you&amp;rsquo;re not limited by poor UI design or bugs in third-party plugins. k6 scripts are Typescript-typed, so most IDEs support autocompletion while you&amp;rsquo;re scripting - the code equivalent of helpful tooltips, except way more universal.
k6 scripts give you the freedom to import and use JS libraries, so you can build on code others have already written. While k6 is not NodeJS, there are even &lt;a href="https://k6.io/docs/using-k6/modules#bundling-node-modules"&gt;some NodeJS packages&lt;/a&gt; that you can bundle for use with your scripts.&lt;/p&gt;
&lt;h3 id="no-external-dependencies"&gt;No external dependencies&lt;/h3&gt;
&lt;p&gt;k6 doesn&amp;rsquo;t require NodeJS or anything else to run, which also reduces the likelihood of bugs that might affect your scripts. For example, since JMeter runs on Java, it is also dependent on Java. When Java receives a new version, that often requires a JMeter update as well. JMeter versions understandably lag a bit behind Java versions as developers refactor code, but this gap can lead to potential security issues in a previous version of Java that may remain unpatched in the meantime.&lt;/p&gt;
&lt;p&gt;With k6, development can proceed independently of any third-party component, so major issues can be addressed more easily.&lt;/p&gt;
&lt;h1 id="comparison-table-jmeter-vs-k6"&gt;Comparison table: JMeter vs k6&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;JMeter&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;k6&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Written in&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scripting language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited: some Java (Groovy, Beanshell, etc)&lt;/td&gt;
&lt;td&gt;Javascript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protocols&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supports most protocols via plugins (native support for HTTP/1.1, SOAP, FTP, JDBC, LDAP, MOM via JMS, SMTP, POP3, IMAP, shell scripts, TCP, Java objects)&lt;/td&gt;
&lt;td&gt;Supports fewer, modern protocols natively (HTTP/1.1, HTTP/2, WebSockets, gRPC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource utilization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://k6.io/blog/static/b4075246f21af514c7a1554d1406b337/d5e13/RPSvMemory.png"&gt;Poor&lt;/a&gt;; One load generator can simulate a few thousand virtual users&lt;/td&gt;
&lt;td&gt;&lt;a href="https://k6.io/blog/static/b4075246f21af514c7a1554d1406b337/d5e13/RPSvMemory.png"&gt;Very good&lt;/a&gt;; One load generator can simulate tens of thousands of virtual users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JVM heap memory must be set&lt;/td&gt;
&lt;td&gt;Uses load generator memory natively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Threading model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1 Thread: 1 Virtual User; Slower performance, higher resource cost&lt;/td&gt;
&lt;td&gt;1 Goroutine: 1 Virtual User; Faster performance, lower resource cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ease of scripting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GUI-driven, with code blocks&lt;/td&gt;
&lt;td&gt;code-driven; VSCode plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test-level thresholds&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No, individual request only&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Script format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;XML&lt;/td&gt;
&lt;td&gt;javascript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Collaboration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Difficult to work on simultaneously; tester-friendly; need the GUI app to edit&lt;/td&gt;
&lt;td&gt;Developer-friendly, easy to version; Javascript format promotes collaboration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Verbose scripts; XML format is difficult to read&lt;/td&gt;
&lt;td&gt;More concise scripts; JavaScript is easy to read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Since 1998; Many third-party tutorials; Extensive documentation; No central community&lt;/td&gt;
&lt;td&gt;Since 2017; Extensive documentation; Fewer third-party tutorials; &lt;a href="https://community.k6.io/"&gt;Official community&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plugin support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires plugins for many features, but there are lots of plugins available&lt;/td&gt;
&lt;td&gt;Most features are natively supported, but plugin support is new and availability is sparse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Native distributed load generation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (Premium only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pre-generated reports&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Default and custom HTML reports; logging via listeners&lt;/td&gt;
&lt;td&gt;No built-in pre-generated reports; integration with analytics tools with third-party dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Website&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://jmeter.apache.org/"&gt;jmeter.apache.org&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://k6.io"&gt;k6.io&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Source Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/apache/jmeter"&gt;Link&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/loadimpact/k6"&gt;Link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The best advice, when attempting to choose a load testing tool, is to do a proof of concept with the most promising candidates. Some features or bugs may prove more or less important when used in an actual testing cycle. However, don&amp;rsquo;t put too much weight on comparisons of the load test &lt;em&gt;results&lt;/em&gt; from two different tools - each tool records metrics differently, and it&amp;rsquo;s more meaningful to compare results against previous runs with the same tool. When switching tools, reestablish a baseline in the new tool each time.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a summary that might help you decide between JMeter and k6.&lt;/p&gt;
&lt;h2 id="both-tools-do-well-at"&gt;Both tools do well at:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Generating protocol-level load on an application server by scripting complex user flows&lt;/li&gt;
&lt;li&gt;Realistic scripting with the use of dynamic think time, test data generation and reuse, and customizable workload models&lt;/li&gt;
&lt;li&gt;Documentation of features and consistency of releases&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="neither-tool-supports"&gt;Neither tool supports:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Generating browser-level load and interacting with DOM elements, especially for SPA&lt;/li&gt;
&lt;li&gt;Detailed results analysis (JMeter has pre-generated HTML reports and listeners, but they are far from ideal). Users should expect to integrate results with databases and data visualization software.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jmeter-is-best-for"&gt;JMeter is best for:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;traditional testing teams&lt;/li&gt;
&lt;li&gt;those looking for a GUI-driven testing tool with tons of third-party tutorials and extensive protocol support&lt;/li&gt;
&lt;li&gt;previous users of commercial tools like LoadRunner and NeoLoad&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="k6-is-best-for"&gt;k6 is best for:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;collaborative, cross-functional engineering teams where testing spans acrosss multiple roles&lt;/li&gt;
&lt;li&gt;those looking for a simple and lightweight, yet fully-featured load testing tool&lt;/li&gt;
&lt;li&gt;teams looking to integrate testing into existing development workflows and CI/CD pipelines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Load testing scripting tools are by no means the most important consideration for the success of load testing. Knowing &lt;em&gt;why&lt;/em&gt; you&amp;rsquo;re testing, what the requirements are, and understanding and communicating results are all arguably more important. The right tool will enable you to address those concerns while providing as little friction as possible. There&amp;rsquo;s no unequivocal &amp;ldquo;best&amp;rdquo; tool; there&amp;rsquo;s only the right tool for &lt;em&gt;your&lt;/em&gt; project and context.&lt;/p&gt;</description></item><item><title>Python for Everybody Specialization</title><link>https://nicolevanderhoeven.com/blog/20210126-python-for-everybody/</link><pubDate>Tue, 26 Jan 2021 23:01:18 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20210126-python-for-everybody/</guid><description>
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/4KQ7OfVztCvpnOBfWcB1p1" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;p&gt;I&amp;rsquo;m starting off the new year by learning something new. I am a big fan of non-traditional education, and &lt;a href="https://coursera.org"&gt;Coursera&lt;/a&gt; is a platform I&amp;rsquo;ve used a few times before. You may not get courses that qualify as university credit, but I&amp;rsquo;ve found it to be a useful resource for learning. I might be biased (my university degree is in Economics, which I&amp;rsquo;ve never used), but in some ways, I actually prefer a MOOC (that&amp;rsquo;s a Massively Open Online Course) to a typical formal university course for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;MOOCs are entirely online, which means it&amp;rsquo;s more accessible - I don&amp;rsquo;t need to physically be in the US to take a course at Harvard&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MOOCs are usually self-paced - perfect for someone working full-time like me&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MOOCs are free or cheap - no huge student loans here; MOOCs are democratizing education and making it available to more people&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Good online education tends to be more practical. There is none of the intellectual posturing that I&amp;rsquo;ve seen in physical classrooms; there are no random classes you need to take to get a fancy piece of paper. I love being able to choose a topic and learn ONLY that&amp;ndash; and get right down to business in the first lesson.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Formal education is often centered around getting students jobs, not helping workers do their jobs well. I already have a job, and I want to learn real, usable skills, not &amp;ldquo;marketable&amp;rdquo; ones.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My main complaint with MOOCs was that they tend to be short (in comparison to a formal course), but Coursera has addressed that somewhat with their specializations. A specialization is a series of courses, but it&amp;rsquo;s more than just disparate courses that are strung together; it&amp;rsquo;s a thoughtfully designed curriculum that builds up on knowledge from previous courses. Last year, I began learning Python.&lt;/p&gt;
&lt;p&gt;Why Python? Others might have different reasons, but here were mine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Python is one of the best languages to learn for data analysis (the other being R). I&amp;rsquo;ve always been interested in data visualization, and as a load tester, I&amp;rsquo;m frequently exposed to large quantities of data that I somehow need to make sense of.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python is pretty good at retrieving data from websites, and I wanted to explore how good it is to use in load testing. I&amp;rsquo;m always eager to learn different ways to test, and I&amp;rsquo;ve wanted to try &lt;a href="https://locust.io/"&gt;Locust&lt;/a&gt; for a while.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python is increasing in popularity, and I wanted to see why.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I decided to take the &lt;a href="https://www.coursera.org/learn/python"&gt;Python for Everybody Specialization&lt;/a&gt; from the University of Michigan (via Coursera) to get an introduction to this cool language. The specialization consists of five modules, each of which I had to take separately:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Programming for Everybody (Getting Started with Python)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python Data Structure&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using Python to Access Web Data&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using Databases with Python&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Capstone: Retrieving, Processing, and Visualizing Data with Python&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The specialization was really fun, and the teacher, Charles Severance, is fantastic. I&amp;rsquo;d taken courses from him before, so I knew what to expect. He didn&amp;rsquo;t disappoint. He has a way of explaining daunting technical concepts in a way that&amp;rsquo;s easy to digest for beginners.&lt;/p&gt;
&lt;p&gt;You can &lt;a href="https://coursera.org/share/11534b2c1b88aa2ab1f0098ebbe32e18"&gt;check out my certificate here&lt;/a&gt;. I&amp;rsquo;m pretty happy with my progress. I&amp;rsquo;m by no means a Python expert now, but I&amp;rsquo;m definitely eager to keep learning how to do more things with this new knowledge.&lt;/p&gt;</description></item><item><title>Cuándo JMeter no alcanza: Pruebas de carga simulando navegadores</title><link>https://nicolevanderhoeven.com/blog/20201216-cuando-jmeter-no-alcanza/</link><pubDate>Wed, 16 Dec 2020 18:18:23 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20201216-cuando-jmeter-no-alcanza/</guid><description>&lt;p&gt;Aquí está el video de mi presentación en JMeter Scripting Day, un evento organizado por &lt;a href="https://jmeterenespanol.org/"&gt;JMeter en Español&lt;/a&gt; el 31 de Oktubre, 2020.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/1KB8e2Ld08M?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Y la versión audio:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/1f17kzrbYmOxk8e65JBTQH" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="introducción"&gt;Introducción&lt;/h2&gt;
&lt;p&gt;Hola a todos! Yo soy Nicole van der Hoeven, y soy performance tester. JMeter es una de mis herramientas favoritas porque es gratis, simple, y por muchos casos hace exactamente lo que quiero y no más. Sin embargo, como performers es parte de nuestro trabajo elegir la herramienta adecuada para cada proyecto, y hay que admitir que hay situaciones en que JMeter simplemente no es suficiente.&lt;/p&gt;
&lt;h2 id="amazon---ui--dev-tools"&gt;Amazon - UI + Dev Tools&lt;/h2&gt;
&lt;p&gt;Esto es el sitio web de Amazon.com. Tengo abierta la pantalla de Chrome Developer Tools, que me muestra las solicitudes (o requests) que componen la página. Cuándo vamos a la página, el navegador (Chrome en este caso) envia estas solicitudes para nosotros, pero eso occure en el fondo. Un usuario normal no las ve.&lt;/p&gt;
&lt;h2 id="jmeter---creando-un-script"&gt;JMeter - Creando un script&lt;/h2&gt;
&lt;p&gt;Lo que JMeter hace es grabar estas solicitudes y replicarlas. Aqui vemos una ejecución de un usuario virtual de un script en JMeter. Cada solicitud tiene la respuesta y el tiempo de respuesta. Pero eso no nos da una idea completa del rendimiento de la aplicación. ¿Por qué?&lt;/p&gt;
&lt;p&gt;Por que JMeter, como otras herramientas de nivel protocolo, tiene unas limitaciónes. La primera es que si hay javascript o otros script que deben estar ejecutado por el navegador, JMeter no los ejecuta. JMeter solo los descarga. El tiempo de respuesta mide la descarga, no la duración de la ejecución. Entonces, siempre habrá una diferencia entre los tiempos de respuesta en una prueba de JMeter y los tiempos de respuesta en práctica.&lt;/p&gt;
&lt;p&gt;A veces esta diferencia no es tan importante. Pero hay más y más aplicaciones hoy en día que se componen principalmente de estos javascript. Por esas aplicaciones, simplemente no es posible hacer un script en JMeter porque la mayoría de la lógica de la aplicación occure en el navegador, no en el servidor.&lt;/p&gt;
&lt;h2 id="flood-challenge"&gt;Flood Challenge&lt;/h2&gt;
&lt;p&gt;La segunda razón es que el aumento de la seguridad de aplicaciones conduce a solicitudes más complejas que incluyen parámetros dinámicos. Estos parámetros hacen que sea más difícil para un intermediario lanzar un ataque, pero también hacen que sea más difícil para nosotros scripters, porque tenemos que extraer información desde la última respuesta y ponerla en la próxima solicitud. Es posible, pero es la pesadilla de cada performance tester.&lt;/p&gt;
&lt;p&gt;Podemos verlo en la aplicación de muestra &lt;a href="https://challenge.flood.io"&gt;Flood Challenge&lt;/a&gt;. Ya no trabajo para Flood, pero todavía es un buen ejemplo.&lt;/p&gt;
&lt;h2 id="correlación-en-jmeter---flood-challenge-script"&gt;Correlación en JMeter - Flood challenge script&lt;/h2&gt;
&lt;p&gt;Esto es mi script en JMeter, y vemos que hay muchos parámetros que he tenido que correlacionar. La solicitud para simular hacer clic en el botón &amp;ldquo;Start&amp;rdquo; necesita los parámetros authenticity token, step ID, y step number, que son dinámicos. El contenido de los parámetros cambian cada vez que usamos esta aplicación.&lt;/p&gt;
&lt;h2 id="la-experiencia-del-usuario"&gt;La experiencia del usuario&lt;/h2&gt;
&lt;p&gt;La tercera razón es que JMeter tampoco mide la verdadera experiencia del usuario. Solo mide la duración de la comunicación entre el navegador y el servidor, pero no mide cosas como la representación de elementos en la página web, ni el efecto de un script que dura mucho tiempo para terminar.&lt;/p&gt;
&lt;p&gt;Eso es importante porque estas cosas afectan significativamente la experiencia del usuario. De hecho, Steve Souders, un conocido el la industría de performance, dice que &lt;a href="https://rigor.com/blog/80-90-of-load-times-are-spent-on-the-front-end-start-there/#:~:text=Start%20there.,-Written%20by%20Craig&amp;amp;text=Last%20month%2C%20Steve%20Souders%2C%20Google's,is%20spent%20on%20the%20frontend"&gt;hasta 90% del tiempo de respuesta&lt;/a&gt; que experimenta el usuario está en la interfaz. Con este numero podemos estar ciertos que una prueba con una herramienta de nivel protocolo no va a poder encapsular el rendimiento final de una aplicación.&lt;/p&gt;
&lt;h2 id="developer-tools---lighthouse"&gt;Developer Tools - Lighthouse&lt;/h2&gt;
&lt;p&gt;Entonces, ¿qué podemos hacer?&lt;/p&gt;
&lt;p&gt;Lo más fácil es intentar de medir la interfaz durante una prueba de cargas. Mientras ejecutamos una prueba con JMeter, podemos ir manualmente a la aplicación web en Chrome para usar Developer Tools. Podemos usar la pestaña Lighthouse, que nos da más métricos del perspectivo del usuario y sugerencias para mejorar el rendimiento.&lt;/p&gt;
&lt;h2 id="webpagetest"&gt;WebPageTest&lt;/h2&gt;
&lt;p&gt;También uso &lt;a href="https://webpagetest.org"&gt;WebPageTest&lt;/a&gt; a veces. Eso tiene más opciones para ejecutar la prueba automáticamente varias veces desde un lugar que puedo elegir. Aún podemos elegir un dispositivo para simular la experiencia de un usuario en un iPad, por ejemplo.&lt;/p&gt;
&lt;h2 id="herramientas-alternativas-para-pruebas-de-carga"&gt;Herramientas alternativas para pruebas de carga&lt;/h2&gt;
&lt;p&gt;Tambien podemos elegir herramientas alternativas para ejecutar nuestras pruebas de carga. Lo que tienen en común es que fueron diseñadas para pruebas de automatización, y utilizan un navegador real.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.selenium.dev/"&gt;Selenium&lt;/a&gt; es probablemente la más popular de esas. Selenium WebDriver en particular. Es compatible con Ruby, Java, Python, C# y Javascript. El problema con Selenium es que puede ser muy pesada. En una machina probablamente podemos ejecutar cinco usuarios de Selenium, más o menos. Eso es importante si estamos tratando de simular miles de usuarios.&lt;/p&gt;
&lt;p&gt;Hay muchas herramientas basadas en Javascript que también podemos usar. Hay varias framework que se utilizan para adaptar Javascript y nodeJS.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Puppeteer fue desarollado por Google. Tricentis Flood también ha diseñado Element que utiliza Puppeteer, pero con un enfoque en el rendimiento.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/microsoft/playwright"&gt;Playwright&lt;/a&gt; es una creación de Microsoft.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://jest.io"&gt;Jest&lt;/a&gt; fue desarrollado por Facebook&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mocha es otro framework que es la base para &lt;a href="https://cypress.io"&gt;Cypress&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Todos esas herramientas son open source y gratis. También hay alternativas propietarias, pero prefiero las soluciones open source porque en la mayoría de casos hacen la misma cosa y se actualizan con más frecuencia.&lt;/p&gt;
&lt;h2 id="element-script"&gt;Element script&lt;/h2&gt;
&lt;p&gt;Esto es un ejemplo de la misma acción, hacer clic en el botón Start en la aplicación Flood Challenge.&lt;/p&gt;
&lt;p&gt;Otra ventaja de simulando navegadores en vez de simulando las solicitudes es que es más simple crear un script. No tenemos que saber cada parámetro. Solo tenemos que describir lo que queremos que cada usuario virtual haga.&lt;/p&gt;
&lt;p&gt;Podemos ejecutar scripts usando estas herramientas al mismo tiempo que un script de &lt;a href="https://jmeter.apache.org"&gt;JMeter&lt;/a&gt; o &lt;a href="https://gatling.io"&gt;Gatling&lt;/a&gt; o lo que sea, y también tenemos la opción de reemplazar los script de protocolo con estas.&lt;/p&gt;
&lt;h2 id="conclusión"&gt;Conclusión&lt;/h2&gt;
&lt;p&gt;Para resumir, JMeter y otras herramientas de nivel protocolo tiene algunas limitaciones.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;No ejecutan scripts del lado del navegador&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pueden ser más complicado correlacionar parámetros dinámicos&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;No miden la experiencia final de un usuario&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Para solucionar estos problemas, podemos complementar pruebas de carga tradicionales, de nivel protocolo, como JMeter, con herramientas simulando navegadores. De este modo podremos tener una comprensión más completa del rendimiento total de la aplicación.&lt;/p&gt;
&lt;p&gt;Bueno, se acabó el tiempo, entonces los dejo aquí. Gracias por escuchar, y me disculpo por mi español. Gracias a todos!&lt;/p&gt;</description></item><item><title>Taking Notes on a Video: my YiNote to Roam Research Workflow</title><link>https://nicolevanderhoeven.com/blog/20201215-yinote-to-roam/</link><pubDate>Tue, 15 Dec 2020 22:00:34 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20201215-yinote-to-roam/</guid><description>&lt;p&gt;Listen to the audio version of this blog post here:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/2TzFxvI8tLNUb3yubhdYvP" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;p&gt;I was working on an article yesterday comparing the performance of k6 with JMeter, and I had a chat to Mihail, one of the devs of k6. He mentioned that a large part of why k6 is so performant compared to other tools (particularly Java-based ones) is that it&amp;rsquo;s written in Go. He talked a bit about Goroutines, and when I told him I was a Go noob, he recommended &lt;a href="https://www.youtube.com/watch?v=YHRO5WQGh0k&amp;amp;ab_channel=GopherAcademy"&gt;this very excellent video&lt;/a&gt; by Kavya Joshi, which explains a bit more about the Go Scheduler and how it manages to achieve performance for Go apps that exceeds the typical 1 Virtual User: 1 kernel thread paradigm that many load testing tools (like JMeter) use.&lt;/p&gt;
&lt;p&gt;Anyway, I got around to watching the video today, and because it was a video I knew I&amp;rsquo;d learn from, I took notes - and ended up improving part of the workflow (more on that later). Other people have commented on my notes before, so I thought I&amp;rsquo;d share how I process videos into my personal wiki, currently held in &lt;a href="https://roamresearch.com"&gt;Roam Research&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="install-yinote-chrome-extension"&gt;Install YiNote Chrome extension&lt;/h2&gt;
&lt;p&gt;I use a free app called &lt;a href="https://yinote.co/"&gt;YiNote&lt;/a&gt; to take video notes in particular. I use &lt;a href="https://chrome.google.com/webstore/detail/yinote/fhpgggnmdlmekfdpkdgeiccfkignhkdf"&gt;their Chrome extension&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="take-notes"&gt;Take Notes&lt;/h2&gt;
&lt;p&gt;I was pretty dubious about using an app just to take notes for video specifically, but there were a few things that sold me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When you click on the text field to take notes in the extension, YiNote automatically pauses the video so that you can type without missing what&amp;rsquo;s being said.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;YiNote also automatically saves the timestamps associated with notes you create, so that you can click on it afterwards to jump to the specific section that you wrote a note on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;YiNote has an export to Markdown option - great compatibility with Roam or &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can also export your notes to a PDF, which is great for sharing. It saves screenshots of the presentation and puts your notes next to the relevant screenshot.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s what it looks like while taking notes:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201215-01.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="export-yinote-notes"&gt;Export YiNote notes&lt;/h2&gt;
&lt;p&gt;After watching the video, I click the Settings wheel from the Chrome extension panel.&lt;/p&gt;
&lt;p&gt;I click the Export as Markdown button.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201215-02.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;That lets me save a &lt;code&gt;.md&lt;/code&gt; file to my local drive. I always save YiNote files in the same folder as the script below.&lt;/p&gt;
&lt;h2 id="run-reformatter-script"&gt;Run reformatter script&lt;/h2&gt;
&lt;p&gt;This is the part that I tweaked today. YiNote&amp;rsquo;s exported Markdown file is formatted in a way that doesn&amp;rsquo;t really play well with Roam. Here&amp;rsquo;s what it looks like, fresh from YiNote:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- Generated by &amp;lt;a href=&amp;#34;https://www.yinote.co/#installation&amp;#34;&amp;gt;YiNote&amp;lt;/a&amp;gt; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- # [GopherCon 2018: Kavya Joshi - The Scheduler Saga](https://www.youtube.com/watch?v=YHRO5WQGh0k) --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- ## [1:19](https://www.youtube.com/watch?v=YHRO5WQGh0k&amp;amp;t=79) --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;The scheduler is what makes sure the Goroutines run concurrently.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!-- ## [1:29](https://www.youtube.com/watch?v=YHRO5WQGh0k&amp;amp;t=89) --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All the information is there, but importing that to Roam, with the comments, isn&amp;rsquo;t really useful. I want to see those timestamps as clickable links, and I want my notes to be nested underneath the corresponding timestamp so I know what belongs where.&lt;/p&gt;
&lt;p&gt;So I dusted off my Python skills tonight and wrote up this little script to do just that. Warning: I am not a developer; I just occasionally code. So I&amp;rsquo;m sure that this isn&amp;rsquo;t the most concise way to do it (contact me and let me know how it could have been better if you have any ideas). But it&amp;rsquo;s functional. And, in the spirit of learning in public, here it is:&lt;/p&gt;
&lt;script src="https://gist.github.com/nicolevanderhoeven/8d089da1617cdbce13dcd406d1d0b960.js"&gt;&lt;/script&gt;
&lt;p&gt;The script results in an output file that is better formatted for Roam.&lt;/p&gt;
&lt;h2 id="import-resulting-file-to-roam"&gt;Import resulting file to Roam&lt;/h2&gt;
&lt;p&gt;On Roam, I hit Options &amp;gt; Import Files, select the output file, hit OK, and that creates a new page in a way better format than the one directly from YiNote. After adding the date, author, and &amp;ldquo;Recommended By&amp;rdquo; fields, this is what it looks like in Roam:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201215-03.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s it!&lt;/h2&gt;
&lt;p&gt;I don&amp;rsquo;t do this for every video I watch, but I find this process useful for more technical videos that I really want to learn from. It&amp;rsquo;s really important to me to get all my notes from disparate sources into Roam, so that they then get processed like any other note into my Zettelkasten - but that&amp;rsquo;s a process for another day.&lt;/p&gt;</description></item><item><title>How I use Roam Research for D&amp;D: Session Notes</title><link>https://nicolevanderhoeven.com/blog/20201214-roam-for-dnd/</link><pubDate>Mon, 14 Dec 2020 20:13:13 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20201214-roam-for-dnd/</guid><description>&lt;p&gt;In case you thought this site was all about performance and load testing, surprise! It&amp;rsquo;s not.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve talked to me personally for longer than ten minutes in the last year, there&amp;rsquo;s a pretty good chance I&amp;rsquo;ve mentioned &lt;a href="https://roamresearch.com/"&gt;Roam Research&lt;/a&gt;, or the note-taking and learning methodology called &lt;a href="https://en.wikipedia.org/wiki/Zettelkasten"&gt;Zettelkasten&lt;/a&gt;. Sorrynotsorry. I&amp;rsquo;ve been obsessed since I discovered it.&lt;/p&gt;
&lt;p&gt;To say Roam Research is a note-taking device is like saying Bitcoin is currency: yes, it IS that, but also so much more. To me, it&amp;rsquo;s my second brain. It&amp;rsquo;s a record of what I know and what I want to know, and sometimes even things I don&amp;rsquo;t know yet. The real reason that Roam and the Zettelkasten idea resonated with me so much is that I can use it a lot for my work. But I&amp;rsquo;ve discovered another use case that Roam is perfect for: Dungeons &amp;amp; Dragons.&lt;/p&gt;
&lt;p&gt;This became longer than I thought it would be, so I&amp;rsquo;ll just talk about how I handle Session Notes in particular.&lt;/p&gt;
&lt;h2 id="starting-a-new-session"&gt;Starting a new session&lt;/h2&gt;
&lt;p&gt;Everything starts with sitting down to play D&amp;amp;D. I write a log of events and information that comes out while I&amp;rsquo;m playing. Here&amp;rsquo;s what a new session log looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-03.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I preface pages in this campaign with &lt;code&gt;D&amp;amp;D/&lt;/code&gt; to make it a separate &lt;a href="https://forum.roamresearch.com/t/namespaces-how-do-they-work/276"&gt;namespace&lt;/a&gt;. A namespace is like a folder, and I use it mainly because I have two D&amp;amp;D campaigns, both of which would have the page &lt;code&gt;Campaign&lt;/code&gt;, &lt;code&gt;Characters&lt;/code&gt;, &lt;code&gt;Locations&lt;/code&gt;, etc, even though the two campaigns are on different worlds. To keep them separate, and to keep from getting mixed up (&lt;em&gt;Was Gloomtoes an NPC in this campaign or that one?&lt;/em&gt;) I use different namespaces for each campaign.&lt;/p&gt;
&lt;p&gt;I add the real-world date in the page name, and then an in-game date reference. The in-game date reference is useful for keeping track of time since I might go a couple weeks (okay, maybe &lt;em&gt;one&lt;/em&gt; week; let&amp;rsquo;s not get too crazy) without playing this campaign, but no time in the in-game world has elapsed. Then, I end the page name with a descriptive title, which I always end up changing after the session. Sometimes I&amp;rsquo;ll title a page &lt;code&gt;Exploring Castle Never&lt;/code&gt;, but then we end up getting carried away in another part of the city instead. That&amp;rsquo;s okay - when I change a page name, Roam then updates all references to that page that I&amp;rsquo;ve made from other pages.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t type all this out manually - I use a &lt;a href="https://textexpander.com/"&gt;TextExpander&lt;/a&gt; template so that to start a new session, all I have to do is create a page in Roam, type &lt;code&gt;;s.dnd&lt;/code&gt; and that is automatically replaced with prompts for a few fields.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Date&lt;/code&gt;: This is the real-world date of when I played this particular session.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Tags&lt;/code&gt;: When I first create session notes, this is prepopulated with &lt;code&gt;#TVZ&lt;/code&gt;, which stands for &lt;em&gt;To Verzettelen&lt;/em&gt;. It&amp;rsquo;s just my personal way of marking a note as one that needs to be processed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Location&lt;/code&gt;: Here I note down the in-game places that my character visited this session. Usually I&amp;rsquo;ll just put city names, but I sometimes also split locations up into districts within a city or rooms within a dungeon.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Characters&lt;/code&gt;: I&amp;rsquo;ve found it helpful to keep track of which characters (players and NPCs) I&amp;rsquo;ve come across in this session.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Session Summary&lt;/code&gt;: This is a section I leave blank when the session is initially created.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="filling-in-the-session-log"&gt;Filling in the session log&lt;/h2&gt;
&lt;p&gt;The rest of the page, after &lt;code&gt;Session Summary&lt;/code&gt;, is where I&amp;rsquo;ll spend most of the time writing. I just write whatever comes to mind and whatever I have time to write, in bullet form. I don&amp;rsquo;t worry about formatting or grammar or linking to other pages. I just type all my thoughts out in plain text.&lt;/p&gt;
&lt;h3 id="hp"&gt;HP&lt;/h3&gt;
&lt;p&gt;I like to play healers, which means I care a lot about how everyone in the party is doing so that I can figure out who best needs healing. &lt;code&gt;{{[[table]]}}&lt;/code&gt; in Roam lets me create a custom table, and I have a &lt;code&gt;;hp&lt;/code&gt; snippet in TextExpander that recreates this table, with all the names of the people currently in my party, so that I just worry about filling in the values. Sometimes I still get it wrong, or miss a bit of damage that the DM calls out, but it&amp;rsquo;s enough to give me an idea of who needs heals.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-04.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I also write down initiative orders, the defenses/vulnerabilities/immunities of monsters we face, descriptions of NPCs we meet, suspicions or questions I might have&amp;hellip; basically anything and everything that comes to mind. Session time is pretty much a brain dump, with no formatting.&lt;/p&gt;
&lt;h3 id="equipment"&gt;Equipment&lt;/h3&gt;
&lt;p&gt;If I acquire a new item during a session, I&amp;rsquo;ll tag that line with &lt;code&gt;#[[D&amp;amp;D/Carrying]]&lt;/code&gt;. This lets me stay on the Session Notes page while still maintaining an up-to-date record of what I&amp;rsquo;m carrying.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-05.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;A tag in Roam is the same as a page, so when I click on that link, it takes me to the &lt;code&gt;D&amp;amp;D/Carrying&lt;/code&gt; page, which conveniently also shows me all the references to that page from other pages.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-06.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Roam shows me not just the page, but the exact block where I made the reference to &lt;code&gt;D&amp;amp;D/Carrying&lt;/code&gt;, so it&amp;rsquo;s easy to see at a glance what I have and where I got it.&lt;/p&gt;
&lt;h3 id="next-actions-and-waiting-for"&gt;Next Actions and Waiting For&lt;/h3&gt;
&lt;p&gt;I use David Allen&amp;rsquo;s &lt;a href="https://gettingthingsdone.com/"&gt;GTD&lt;/a&gt; methodology in D&amp;amp;D too. When I want to remember to do something later, I tag it with &lt;code&gt;#[[D&amp;amp;D/Agenda]]&lt;/code&gt; and &lt;code&gt;#[[Next Actions]]&lt;/code&gt; or &lt;code&gt;#[[Waiting For]]&lt;/code&gt;. This presents me with the same opportunities as &lt;code&gt;D&amp;amp;D/Carrying&lt;/code&gt;, where I can see references to those pages later as well.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-07.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="processing-the-session"&gt;Processing the session&lt;/h2&gt;
&lt;p&gt;Sometime after the session, I&amp;rsquo;ll go and fill in the metadata from my template, including the session summary, which is just a paragraph or two describing what happened in the session at a high level. This is mainly to jog my memory in the next session, when I&amp;rsquo;ve already forgotten what we did in the previous one.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what it looks like, all filled in:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-02.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="adding-links-and-fleshing-out-pages"&gt;Adding links and fleshing out pages&lt;/h3&gt;
&lt;p&gt;I then go back through the notes themselves and add turn any people, places, or interesting objects that I mention into links. In Roam, that&amp;rsquo;s a matter of highlighting them and hitting &lt;code&gt;[[&lt;/code&gt;. Then, I click on the page I&amp;rsquo;ve just created and try to add information that I&amp;rsquo;ve learned. This isn&amp;rsquo;t strictly necessary, since all mentions of that page will show up as a linked reference, but it&amp;rsquo;s an opportunity to consolidate my information on that subject and see how it fits into what else I&amp;rsquo;ve written there.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a page for an NPC:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-09.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Having these links means that when an NPC says &amp;ldquo;I&amp;rsquo;m from Leilon&amp;rdquo;, I can type &lt;code&gt;Leilon&lt;/code&gt; into Roam:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-08.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&amp;hellip; and say, &amp;ldquo;Oh, Leilon. I heard you had a dragon problem there recently. Did your family get out okay?&amp;rdquo; Boom! Instant advantage on a Diplomacy check.&lt;/p&gt;
&lt;p&gt;Sometimes we&amp;rsquo;re in a dungeon with many rooms, and this is the point where I draw a quick map on my iPad Pro and Apple Pencil using &lt;a href="https://www.gingerlabs.com/"&gt;Notability&lt;/a&gt;, and then I upload it right from my iPad to Roam.&lt;/p&gt;
&lt;p&gt;This map is in my page for Castle Never:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-10.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The little blue &amp;ldquo;8&amp;rdquo; on the upper left of the map is versions. Information rarely comes all at once in D&amp;amp;D; it usually comes in trickles. As I learned more about the castle, I revised the map and added a new version to it, so that I can still see previous versions and what I thought the map was like when we first explored it.&lt;/p&gt;
&lt;p&gt;At this stage, I also add any tags I forgot to add during the session.&lt;/p&gt;
&lt;h3 id="reviewing-quests-and-questions"&gt;Reviewing quests and questions&lt;/h3&gt;
&lt;p&gt;After I go through the session notes, I go to the page called &lt;code&gt;D&amp;amp;D/Quests and Questions&lt;/code&gt;, where I keep a running list of all the open quest hooks and any unanswered questions I might have.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-11.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Not everything on this page is meant to be resolved, but I find it comforting to track everything anyway, in case I happen to find myself in a city with nothing in particular to do. I mark these with a checkbox (&lt;code&gt;{{[[TODO]]}})&lt;/code&gt; in Roam) and tick the box when one is resolved. Roam lets me filter out the resolved quests so I only see things that are outstanding.&lt;/p&gt;
&lt;p&gt;This is where I get the most value out of taking notes. It&amp;rsquo;s at this point where I&amp;rsquo;ll add our progress towards quests, make connections between two seemingly disconnected quests, and situate what I&amp;rsquo;ve just learned in the session within the broader context of the world and the campaign. I also review and update items on the Next Actions and Waiting For.&lt;/p&gt;
&lt;p&gt;This is the part that takes the longest, but it&amp;rsquo;s also what most prepares me for when I get to the table again next week.&lt;/p&gt;
&lt;h2 id="tying-it-all-together"&gt;Tying it all together&lt;/h2&gt;
&lt;p&gt;I use a &lt;code&gt;D&amp;amp;D/Campaign&lt;/code&gt; page as my landing page for this campaign. It includes &lt;code&gt;D&amp;amp;D/Quests and Questions&lt;/code&gt; as well as a list of all sessions to date, so that I always know what happened and when.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20201214-01.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="why-do-i-take-notes-for-a-game"&gt;Why do I take notes for a game?&lt;/h2&gt;
&lt;p&gt;You might be wondering why I go to so much trouble. Why do I take complicated notes about something I do &amp;ldquo;just&amp;rdquo; for fun?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Taking notes helps me offload this all safely from my brain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I take my fun as seriously as I take my work.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Week of Testing: Intro</title><link>https://nicolevanderhoeven.com/blog/20201123-week-of-testing-intro/</link><pubDate>Mon, 23 Nov 2020 20:45:31 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20201123-week-of-testing-intro/</guid><description>&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/zcYeboT5FYE?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Today was my first day at &lt;a href="https://k6.io"&gt;k6.io&lt;/a&gt;! I&amp;rsquo;m happy to announce that I&amp;rsquo;ve joined as their newest Developer Advocate for Performance Testing. k6 is a company I&amp;rsquo;ve had my eye on for a while, even (or maybe especially) when I was working for a competitor, &lt;a href="https://flood.io"&gt;Tricentis Flood&lt;/a&gt;. I always admired k6&amp;rsquo;s stance on open source, their involvement with the developer community, and the slick web app that brings load testing into the hands of many developers and testers.&lt;/p&gt;
&lt;p&gt;As a load tester, I try to be tool-agnostic. I have to admit, though, that I do have a strong bias towards opensource tools. I joined Flood because of their focus on tools I already loved like &lt;a href="http://jmeter.apache.org/download_jmeter.cgi"&gt;JMeter&lt;/a&gt;, &lt;a href="https://gatling.io/"&gt;Gatling&lt;/a&gt;, and &lt;a href="https://element.flood.io"&gt;Element&lt;/a&gt; (based on Puppeteer). (And Selenium, but it&amp;rsquo;s a stretch to say I love Selenium.) But these are tools I&amp;rsquo;ve used for years, most of them from even before I joined Flood. What kind of load tester would I be if I could only really talk about scripting in a handful of tools?&lt;/p&gt;
&lt;p&gt;So I thought it was time for a change, and k6 was the first and only company on my list for a couple reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/loadimpact/k6"&gt;The free and open source tool k6&lt;/a&gt;, which recently crossed over to 10,000 stars on Github, is often touted as the developer&amp;rsquo;s load testing tool of choice.&lt;/li&gt;
&lt;li&gt;k6&amp;rsquo;s &lt;a href="https://k6.io/cloud"&gt;cloud offering&lt;/a&gt; integrates seamlessly with the OSS tool, and it adds some killer features like performance insights that add real value to teams wanting to scale their load tests.&lt;/li&gt;
&lt;li&gt;The k6 team is incredibly responsive to the needs of their community. Unlike most companies, where only marketing, sales, and customer support people talk to customers, k6 developers are active on GitHub, social media, and other blog sites. Everyone is passionate about what they&amp;rsquo;re building.&lt;/li&gt;
&lt;li&gt;Roles in testing are changing, and the lines between tester and developer are blurring. Developers who don&amp;rsquo;t test are going to become just as obsolete as testers who don&amp;rsquo;t develop. I see k6&amp;rsquo;s popularity among the developer crowd as a good opportunity for me to sharpen my coding skills.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I had to see for myself whether k6 was actually as good as it seemed.&lt;/p&gt;
&lt;p&gt;Luckily, it turns out that k6 has a tradition of a &amp;ldquo;Week of Testing&amp;rdquo;, where every new member of the k6 team, regardless of role, spends their first week putting the product through its paces. Every part of the whole k6 solution (Cloud and OSS tool) is fair game, and the goal of the Week of Testing is to present feedback to the team at the end of it, while the new member&amp;rsquo;s eyes are fresh. When they told me about it, they said &amp;ldquo;We want to see you learn the tool on your own to see what parts are difficult to figure out.&amp;rdquo; To which I responded, &amp;ldquo;Challenge accepted!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I decided to not only give a presentation at the end, but also to video the process for posterity&amp;rsquo;s sake&amp;hellip; and, let&amp;rsquo;s face it, to impress my new managers and coworkers with my dazzling work ethic.&lt;/p&gt;
&lt;p&gt;Step 1: Get the company&amp;rsquo;s name right.&lt;/p&gt;
&lt;p&gt;In the first video, I share the differences between k6 vs. k6 Cloud vs. Load Impact!&lt;/p&gt;</description></item><item><title>How to Become a Load Tester</title><link>https://nicolevanderhoeven.com/blog/20201007-how-to-become-a-load-tester/</link><pubDate>Wed, 07 Oct 2020 14:15:45 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20201007-how-to-become-a-load-tester/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/how-to-become-a-load-tester"&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://www.flood.io/blog/how-to-become-a-load-tester"&gt;Originally posted here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Load testing can be a pretty intimidating field to get into, but it&amp;rsquo;s easy to get started. Here are some concrete steps you can take to start learning how to load test from scratch.&lt;/p&gt;
&lt;p&gt;In this video, Flood co-founder Tim Koopmans and I teamed up at Tricentis User Conference 2020 to talk about the essential ingredients for load testing.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_R2dmEmtO48?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="background-required-for-load-testing"&gt;Background required for load testing&lt;/h2&gt;
&lt;p&gt;Load testing can be quite technical. It involves not just the ability to use different programming languages to create load testing scripts but also familiarity with software engineering practices, DevOps, application infrastructure, site reliability, and networking. A good load tester needs to be able to understand the basics of how an application works and how different components interact with each other. It&amp;rsquo;s common for a load tester to work on applications employing a variety of technologies, so staying up to date with recent technological trends and switching seamlessly from one language to another, or from one platform to another, is of utmost importantance. This technical side of load testing might make you think that a load tester needs to have formal studies in computer science and several years of experience in software testing. The reality is that there are many load testers who stumbled into the field from wildly unexpected backgrounds. So you might think load testing looks like this:&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@wocintechchat?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Christina @ wocintechchat.com&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/meeting?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And sometimes it does. But it more often looks something like this:&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester02.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@youxventures?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;You X Ventures&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/meeting?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You see, load testing is an interdisciplinary field. So much of the job involves talking to people. Sometimes the requirements for a project are not clear, and someone needs to talk to the both the business and the developers to make sure that nonfunctional requirements are clarified. A load tester is a detective, tracing a request as it goes from one application component to another, coordinating with different application teams. A load tester is a scientist, using the scientific method to design experiments that yield the results. A load tester is a statistician, making sense of a jumble of data and seeking correlations between variables. A load tester is a public speaker. Even when the actual load testing has been completed and the report has been sent, a load tester makes sure all stakeholders understand the report so that the results can be used to make meaningful changes to improve application performance. All these tasks require a mishmash of skills and competencies that can be difficult to find in just the field of computer science.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take our backgrounds as anecdotal examples.&lt;/p&gt;
&lt;p&gt;Tim grew up and currently lives in a small country town in Australia, and he joined the military straight after school. He later developed a love of automating things, which led him first to use tools like perl to scrape websites and try to win bids with early versions of ebay, and then later to starting a performance engineering consulting career and co-founding Flood itself.&lt;/p&gt;
&lt;p&gt;I, Nicole, grew up in Metro Manila in the Philippines (which has 14,000 more people per km2 than Mumbai does). I learned English as a foreign language in school, and I have a university degree in Economics. When I discovered load testing, I had a job in a completely different field– I was working for an airline and making sure engine components were properly maintained and repaired. My love for gadgets and technology took me down an 8-year rabbit hole involving doing performance consulting for clients across a variety of industries in Australia and the Netherlands. At Flood, I help customers around the world improve their application performance.&lt;/p&gt;
&lt;p&gt;As you can see, Tim and I came from very different backgrounds and different parts of the world. Neither of us knew what load testing even was when we started working. Neither of us got formal instruction in load testing. But somehow, we still both ended up becoming load testers at Flood.&lt;/p&gt;
&lt;h2 id="reasons-to-become-a-load-tester"&gt;Reasons to become a load tester&lt;/h2&gt;
&lt;p&gt;So why become a load tester? Here are some practical reasons.&lt;/p&gt;
&lt;h3 id="load-testing-is-cool"&gt;Load testing is cool&lt;/h3&gt;
&lt;p&gt;A little-known fact about load testing is that it can be really satisfying to be involved in big-name projects that end up going live.&lt;/p&gt;
&lt;p&gt;You may have heard of Flood. But even if you haven’t, we’re sure you&amp;rsquo;re familiar with the results of load testing done on Flood. In the last few years, we have been helping Hotstar, one of the largest streaming services in the world, load test their systems in preparation for the Indian Premier League.&lt;/p&gt;
&lt;p&gt;Hotstar uses an open-source tool that we support, Gatling, to create scripts to simulate users tuning in to watch cricket games. Then, they use Flood to create a “Game Day” simulation. They scale up those load tests and run 5 million users on thousands of machines in the cloud. We help them coordinate execution, spot problems quickly with real-time results from all those machines while the test is still running, and identify bottlenecks so that they don’t occur in production, when India’s playing.&lt;/p&gt;
&lt;p&gt;As a load tester, you&amp;rsquo;ll definitely be working with brands you recognize and love.&lt;/p&gt;
&lt;h3 id="load-testing-can-pay-well"&gt;Load testing can pay well&lt;/h3&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester03.png" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://www.payscale.com/research/IN/Job=Software_Tester/Salary"&gt;Payscale&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester04.png" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://www.payscale.com/research/IN/Job=Performance_Engineer/Salary"&gt;Payscale&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I said this would be a practical list, right?&lt;/p&gt;
&lt;p&gt;Here are 261,768 practical reasons. The average performance engineer’s salary is 71% more than that of an average software tester in India, according to Payscale.com. We all know that money isn’t everything, but this is a clear signal from the market about the importance of performance-related skills. Adding load testing to your CV as a tester makes you more attractive to employers. It’s a great way to differentiate yourself in an over-saturated market.&lt;/p&gt;
&lt;h3 id="load-testing-can-literally-take-you-places"&gt;Load testing can literally take you places&lt;/h3&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester05.png" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://twitter.com/shamim_j"&gt;Shamim&lt;/a&gt; from &lt;a href="https://www.flood.io/blog/inside-the-career-of-a-digital-nomad-is-a-life-on-the-road-right-for-you"&gt;Flood&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Then there are the side benefits of being in load testing. The photos above are real ones from the Flood team. That’s me on the upper left, exploring a castle in Belgrade, Serbia, and that’s Tim in the bottom right, taking a photo in San Francisco, USA.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.flood.io/blog/inside-the-career-of-a-digital-nomad-is-a-life-on-the-road-right-for-you"&gt;We’re a fully distributed team on Flood&lt;/a&gt;, which means we can work from anywhere. Flood is a web app, so you don’t need to install anything– I’ve been known to start load tests from my iPad in a café. Load testing on the cloud frees you from being tied to running tests from the office. You can start load generators when you need them and shut them down when you don’t– all from a web browser. It’s a job that can come with a great work/life balance, if you find the right company.&lt;/p&gt;
&lt;p&gt;Load testing can also be a great way to move to another country. It’s what’s allowed me to move to other countries– twice. In Australia, the average performance engineer’s salary is AU$108,053, or over 5.6 million rupee.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester06.png" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://www.payscale.com/research/AU/Job=Performance_Engineer/Salary"&gt;Payscale&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So it’s clear that there are some pretty good reasons for you to become a load tester. I&amp;rsquo;m going to walk you through the practical steps to become a load tester. You may not be an expert by the end of this, but I hope I can convince you that you can get started in load testing whether your background looks like Tim’s or mine.&lt;/p&gt;
&lt;h3 id="load-testing-adds-real-value"&gt;Load testing adds real value&lt;/h3&gt;
&lt;p&gt;And the trite, but absolutely true reason: Everyone fails.&lt;/p&gt;
&lt;p&gt;As a load tester, you are a gatekeeper of quality. What you do directly affects revenue and customer experience.&lt;/p&gt;
&lt;p&gt;Performance is an aspect of software that is often overlooked, but it’s only becoming more important in a world where everything is going online. People are being born who have never known what it’s like not to have the internet, and consumers in general expect applications to be responsive and stable. Would-be customers who can’t get an e-commerce site to load within 3 seconds may switch to a competitor’s site, and we all know there are many of them. Performance-related outages can be seriously damaging to a company’s reputation, and social networking amplifies customer dissatisfaction by effectively giving everyone a megaphone to complain into.&lt;/p&gt;
&lt;p&gt;Software functionality is still important. However, if an online store goes live with no critical functional defects, but it fails when the 100th person logs into it, or it takes 10 seconds to load the homepage, can you really say that it was ready for production?&lt;/p&gt;
&lt;p&gt;Load testing is a crucial part of ensuring software quality, and quality is the responsibility of everyone on a team, even if the word “performance” isn’t in your job title.&lt;/p&gt;
&lt;h2 id="the-essential-ingredients-of-a-load-test"&gt;The essential ingredients of a load test&lt;/h2&gt;
&lt;p&gt;Load testing is like cooking a meal. This is one of Tim&amp;rsquo;s favorite dishes, aloo matar.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester07.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://www.cubesnjuliennes.com/aloo-matar-recipe/"&gt;Cubes n Juliennes&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can approach cooking as a science. There are recipes with a list of ingredients, a precise measurement of how much you need for each, and a procedure for creating the dish.&lt;/p&gt;
&lt;p&gt;But you can also look at cooking as an art. Cooking the same dish over and over again will give you an experience in working with those ingredients that a novice cook won’t have. There’s room in cooking for adding one ingredient, changing the quantity of another, or just trying something new.&lt;/p&gt;
&lt;p&gt;A master chef knows how to use both the science and the art to create something delicious. But you don’t HAVE to be at that level in order to cook.&lt;/p&gt;
&lt;p&gt;Anyone can cook. If you know how to follow a basic recipe, you may not create a masterpiece, but if you’re like me and you haven’t had lunch yet, you’ll still end up with something you’ll eat.&lt;/p&gt;
&lt;p&gt;So here’s the recipe for load testing.&lt;/p&gt;
&lt;p&gt;I prefer kadai paneer myself, but aloo matar requires, among other things, peas. And so does load testing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P is Preparation&lt;/strong&gt;. To prepare for a load test, you need to get your ingredients ready. You need to decide what it is you want to test, why you’re testing it, and how. You write load testing scripts that will simulate real users as closely as possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;E is Execution&lt;/strong&gt;. This is when you do the actual cooking. You run your load tests and scale them up according to your requirements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A is Analysis&lt;/strong&gt;. You figure out whether the dish you created is edible. You determine how your application servers responded during the load test, and decide if that’s acceptable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Preparation – Execution – Analysis.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="preparing-for-a-load-test"&gt;Preparing for a load test&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“As far as the World Cup is concerned, it is a process. We don’t want to jump to the 50th floor straight away. We must start on the ground floor.” - Sachin Tendulkar, via &lt;a href="https://www.espncricinfo.com/story/_/id/22538610/world-cup-final-dream-tendulkar"&gt;ESPN&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Load testing doesn’t start with writing a script. Load testing starts with building a foundation for later tests.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester08.png" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://www.insidesport.co/cricket-sachin-tendulkar-comes-to-aid-of-ailing-ashraf-who-once-fixed-his-bats/"&gt;Inside Sport&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Part of that foundation is asking WHY. Why do you want to do a load test? Has there been a performance-related issue before? Are you trying to determine whether an application is ready to go live, or is it already live, and you just want to improve its performance? What type of application do you want to test?&lt;/p&gt;
&lt;p&gt;WHOM are you cooking for? I think you’d cook differently for yourself than you would for Sachin Tendulkar. What do your end users typically do on your site? Where do they come from?&lt;/p&gt;
&lt;p&gt;These are questions that need to be answered before you think about scripting. Here are some resource for planning your load tests:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.flood.io/blog/start-with-why-how-to-write-requirements-for-api-load-testing"&gt;Start with Why: How to write requirements for API load testing&lt;/a&gt;
&lt;a href="https://www.flood.io/blog/how-to-write-a-test-plan-for-load-testing"&gt;How to write a test plan for load testing&lt;/a&gt;
&lt;a href="https://guides.flood.io/ebooks/api-load-testing/scripting-an-api-load-test"&gt;Selecting a tool for load testing&lt;/a&gt;
Generating a load testing script without coding: &lt;a href="https://guides.flood.io/overview-of-flood/scripting-your-load-test"&gt;Getting Started with Test Builder&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="execution-of-a-load-test"&gt;Execution of a load test&lt;/h2&gt;
&lt;p&gt;So now we’re ready to start cooking. This is the Execution phase. You’ll need a machine to use as a load generator. Depending on the number of users you want to run with, you may need more than one machine. You might be tempted to run a load test on your laptop, or on a bunch of machines you have lying around, but that introduces new variables that may affect the results.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester09.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://unsplash.com/@suicide_chewbacca?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Ashwini Chaudhary&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/indian-food?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Like any good cook, you’ll also need to make sure you have all your ingredients handy. In load testing, that means making sure your scripts and test data files are ready to be executed on every load generator.&lt;/p&gt;
&lt;p&gt;Both of these problems – provisioning a load generator and transferring files over to them – are solved pretty easily by running on the cloud using something like Flood.&lt;/p&gt;
&lt;p&gt;Designing and executing your load test&lt;/p&gt;
&lt;p&gt;Where should you run your load tests? A look at demand, hosted, and on-premise infrastructure&lt;/p&gt;
&lt;h2 id="analysis-of-results"&gt;Analysis of Results&lt;/h2&gt;
&lt;p&gt;The last part is Analysis.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester10.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://www.finedininglovers.com/recipes/brunch/potato-pakoras-yogurt-sauce"&gt;Fine Dining Lovers&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a pretty crucial moment, because this is when you figure out whether what you cooked was delicious or not. Unfortunately, in load testing, it’s not always easy to tell how your test went. It’s like this photo of pakora (onion is my favorite kind). It’s kind of hard to say what expression that is in their faces. Is it disgust? Or is the pakora so yummy that they just can’t wait to eat more?&lt;/p&gt;
&lt;p&gt;Load testing typically generates a LOT of data. You’d have to go copy every single file from every load generator you used. But can you imagine the hundreds of load generators that Hotstar used? Of course they didn’t manually copy those files. Instead, they used Flood.&lt;/p&gt;
&lt;p&gt;On Flood, we show real-time graphs as you’re running the test so that you can tell immediately whether transactions are failing or whether a server has stopped responding.&lt;/p&gt;
&lt;p&gt;After the test, you can also compare this data to previous tests to spot trends, or even create your own dashboards using an analytics engine.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://guides.flood.io/results-analysis/metrics-during-test"&gt;Real-time metrics during your load test&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://guides.flood.io/results-analysis/archived-results"&gt;Downloading results and logs after the test&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="is-that-it"&gt;Is that it?&lt;/h2&gt;
&lt;p&gt;You’ve prepared for a load test, you’ve executed a load test, and you’ve analysed the results from the load test. Congratulations, you’re a load tester! By following this recipe, you’re already doing better than many people, and you’re certainly doing a lot better than those who do not run any load tests at all.&lt;/p&gt;
&lt;p&gt;The cool thing about load testing, though, is that this is just the beginning. There’s always room for improvement.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-become-a-load-tester11.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://popupcity.net/observations/dabbawalla-hot-lunch-delivery-by-mumbais-fastest/"&gt;Pop Up City&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Dabbawallas are people who deliver hot lunches, and while the concept is not new, the implementation in India is particularly interesting.&lt;/p&gt;
&lt;p&gt;For these dabbawallas, having the meals prepared is not the end of the job. Once the food is ready, it needs to be packed into lunchboxes, picked up, separated according to destination, put on trains, and delivered. Later, the empty lunchboxes are picked up, and the system works in reverse.&lt;/p&gt;
&lt;p&gt;It turns out that this is actually a pretty sophisticated system. In fact, it’s been estimated that only one tiffin every two months is not delivered. That’s equivalent to a &lt;a href="https://www.bbc.com/future/article/20170114-the-125-year-old-network-that-keeps-mumbai-going"&gt;Six-Sigma quality rating of 99.9999999%&lt;/a&gt; (that’s nine nines) or more.&lt;/p&gt;
&lt;p&gt;Dabbawallas didn’t get that rating just by delivering quickly. They did it by being consistent and reliable. They did it by having a process.&lt;/p&gt;
&lt;p&gt;Once you see the benefits of running a load test, you may also start to understand that the real value of load testing is not in a test that you run once whenever you think of it. Imagine having the benefits of a load test tied to your development process, so that it is automated.&lt;/p&gt;
&lt;p&gt;As an advanced topic, &lt;a href="https://www.flood.io/blog/load-testing-in-devops-pipelines-with-azure-and-flood"&gt;adding your load testing&lt;/a&gt; to your team&amp;rsquo;s Continuous Integration/Continuous Development pipelines such as sets up a repeatable process that reaps benefits far into the future, not just whenever official load testing activities are carried out.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://guides.flood.io/continuous-integration/what-is-continuous-integration"&gt;Continuous Integration with Flood&lt;/a&gt;: Integrating Travis CI, Circle CI, and Jenkins&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So that’s the recipe for becoming a load tester.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Preparation – Execution – Analysis.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Preparation&lt;/strong&gt;, you determine why you’re testing and what tool best fits your needs. We showed you how to use Element, but there are many open source tools out there. Don’t get overwhelmed by scripting– a lot of times it can just mean replacing a URL with the one YOU want to test.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Execution&lt;/strong&gt;, you run a baseline test that will serve as a point of comparison for future testing, choose the number of users and the number of regions you want, and then you run your test. Running on the cloud is the easiest (and usually cheapest) way to get started.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Analysis&lt;/strong&gt;, you view the results in real-time, compare historical performance to see trends, and share reports to stakeholders so that your team can make changes to your application.&lt;/p&gt;
&lt;p&gt;We also talked about the bonus topic of &lt;strong&gt;CI/CD&lt;/strong&gt; and how you can incorporate load testing into your pipeline to get a continuous feedback during your development cycle.&lt;/p&gt;
&lt;p&gt;Tim and I started in very different places and had different experiences before we discovered load testing. But eventually, we ended up on the same path, and that’s what we’ve outlined for you here. We hope that this convinces you that load testing doesn’t have to be complicated.&lt;/p&gt;
&lt;p&gt;You absolutely don’t need to be an expert to run a load test. Even if you don’t think you’ll get all these steps right, a load test that is less than ideal is still better than no load testing at all.&lt;/p&gt;
&lt;p&gt;In the end, anyone can be a load tester with the right attitude and the right tools.&lt;/p&gt;
&lt;h2 id="learning-about-load-testing"&gt;Learning about load testing&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.goodreads.com/book/show/1681559.High_Performance_Web_Sites"&gt;High Performance Websites&lt;/a&gt; book by Steve Souders&lt;/p&gt;
&lt;p&gt;Ministry of Testing/PerfBytes &lt;a href="https://www.ministryoftesting.com/dojo/lessons/a-quick-start-guide-to-learning-performance-testing"&gt;list of resources&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.perfbytes.com/"&gt;PerfBytes podcast&lt;/a&gt; (EN and ES)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://guildconferences.com/conference/perfguild-2020/"&gt;PerfGuild conferences&lt;/a&gt; by Joe Colantonio&lt;/p&gt;
&lt;p&gt;Señor Performo’s YouTube Channel: &lt;a href="https://www.youtube.com/channel/UCO-RlGccAUQnMqTaBkvUJhQ"&gt;Señor Performo ENG&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Scott Moore’s &lt;a href="https://www.youtube.com/watch?v=9H2GSGvGwqQ&amp;amp;list=PLXRCdih3vTUEFz1TVyP7RRqEEk__CYGoB"&gt;Performance Tour&lt;/a&gt; on YouTube&lt;/p&gt;</description></item><item><title>How to Write a Load Test Plan</title><link>https://nicolevanderhoeven.com/blog/20200921-how-to-write-a-load-test-plan/</link><pubDate>Mon, 21 Sep 2020 16:33:23 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200921-how-to-write-a-load-test-plan/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/how-to-write-a-test-plan-for-load-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/how-to-write-a-test-plan-for-load-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;People often start load testing by creating a script in their favorite load testing tool, but good load testing starts before that. Planning a load test may seem like a tedious exercise, but it is essential to ensuring the success of your testing.&lt;/p&gt;
&lt;p&gt;A test plan should answer why, what, who, when, and how the testing will be carried out.&lt;/p&gt;
&lt;p&gt;A good test plan, particularly for load testing, includes the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requirements&lt;/li&gt;
&lt;li&gt;Scope&lt;/li&gt;
&lt;li&gt;Entry criteria&lt;/li&gt;
&lt;li&gt;Workload modeling&lt;/li&gt;
&lt;li&gt;Server monitoring&lt;/li&gt;
&lt;li&gt;Test doubltes&lt;/li&gt;
&lt;li&gt;Test Scenarios&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;ll dive into each of these, but first, a note on the format of a test plan.&lt;/p&gt;
&lt;h2 id="test-plan-format"&gt;Test Plan Format&lt;/h2&gt;
&lt;p&gt;Test plans have been traditionally presented as a document, but there&amp;rsquo;s no hard-and-fast rule. Instead, the length of a test plan, and the level of detail it goes into, should be tailored for the complexity of the project and the backgrounds of the stakeholders.&lt;/p&gt;
&lt;p&gt;Teams using Agile methodologies may not have a formal test plan at all, but I would argue that all teams should still have some sort of plan, whether it&amp;rsquo;s a Word document or a list of bullet points. Written plans clarify and communicate intent, and testing can be an expensive and fruitless exercise without a consensus on its intent.&lt;/p&gt;
&lt;p&gt;Technical documentation can be difficult to digest and understand, and that&amp;rsquo;s dangerous when it comes to software development and testing, where it&amp;rsquo;s imperative that all stakeholders are on the same page about the state of an application.&lt;/p&gt;
&lt;p&gt;Paul McLean, a performance engineer, has a creative solution for this. He creates a companion video for every technical report, giving stakeholders the option to read the written report for details but watch the video for a more high-level and nuanced explanation.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of a companion video he created for a test plan:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/FJj_7nyV3Ak?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="requirements"&gt;Requirements&lt;/h2&gt;
&lt;p&gt;Everything starts with requirements. Until requirements are identified, load testing can only ever be aimless and explanatory, and any performance bottlenecks spotted will be incidental.&lt;/p&gt;
&lt;p&gt;Requirements inform every step of the load testing process. Why are we doing load testing? What exactly do we want to test? How will we know when a test has passed or failed? How will we know if application performance is good enough to go into production? What does “good enough” mean?&lt;/p&gt;
&lt;p&gt;This is such an important part of the planning process that I felt it deserved its own blog post. See it here: &lt;a href="https://www.flood.io/blog/start-with-why-how-to-write-requirements-for-api-load-testing"&gt;Start with Why: How to Write Requirements for API Load Testing&lt;/a&gt;. Even though I specifically mention API load testing, the principles apply to load testing in general.&lt;/p&gt;
&lt;h2 id="scope"&gt;Scope&lt;/h2&gt;
&lt;p&gt;At Flood, we use a product development methodology called &lt;a href="https://basecamp.com/shapeup"&gt;Shape Up&lt;/a&gt; to keep us on track as we build new features. Projects in Shape Up are called &amp;ldquo;shaped work&amp;rdquo;, and one of the basic properties of shaped work is that it is bounded.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip;shaped work [also] indicates what not to do. It tells the team where to stop. There’s a specific appetite—the amount of time the team is allowed to spend on the project. Completing the project within that fixed amount of time requires limiting the scope and leaving specific things out.- Ryan Singer, &lt;a href="https://basecamp.com/shapeup"&gt;Shape Up: Stop Running in Circles and Ship Work that Matters&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Defining scope is setting boundaries. Business priorities need to be weighed against the resource limitations (number of people available to do the work and time available) in order for testing to deliver maximum value.&lt;/p&gt;
&lt;p&gt;Some considerations for scope include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Specific features or key transactions to be tested&lt;/li&gt;
&lt;li&gt;Types of tests included (component test vs end-to-end test)&lt;/li&gt;
&lt;li&gt;Test scenarios (peak load test vs disaster recovery)&lt;/li&gt;
&lt;li&gt;Applications included in testing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As with most things in the planning phase, scope is something that can change during the test when unexpected circumstances arise or when priorities change. But it&amp;rsquo;s still a good practice to define the scope at the beginning, and update it as it changes.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/how-to-write-a-load-test-plan01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s important, however, to &lt;em&gt;prune with intention&lt;/em&gt;. By keeping our objectives in mind, we can keep our work in line with our intentions.&lt;/p&gt;
&lt;h2 id="entry-criteria"&gt;Entry criteria&lt;/h2&gt;
&lt;p&gt;Occasionally, you may be asked to load test an application that isn&amp;rsquo;t ready to be load tested. This happens more often than you think. While it&amp;rsquo;s true that Performance Testing is something that should be baked into all software development from the beginning, this doesn&amp;rsquo;t hold true for Load Testing. Load testing is only one activity that falls underneath the broader umbrella of performance testing.&lt;/p&gt;
&lt;p&gt;Entry criteria are conditions that you need to be fulfilled before the testing actually begins. It’s a good idea to have these conditions communicated beforehand so that everyone is clear on what needs to be set up before you can do your job.&lt;/p&gt;
&lt;h3 id="functional-testing-does-the-application-work"&gt;Functional testing: Does the application work?&lt;/h3&gt;
&lt;p&gt;Load testing cannot realistically be carried out until at least the core functionality has been tested and high-severity defects have been fixed. Depending on the kind of load test you want to execute, you may also want to specify that UAT has been executed, as there’s no point doing an end-to-end load test with 1000 users if it doesn’t work for one user.&lt;/p&gt;
&lt;h3 id="load-testing-environment-is-it-production-like"&gt;Load Testing Environment: Is it production-like?&lt;/h3&gt;
&lt;p&gt;Nonfunctional testing has stricter requirements for an environment than does functional testing, and you may have to champion this cause. For load testing, it is not enough to have an application staging environment that is a virtual machine that is a quarter of the size of the production environment. It’s important to get as close to a production-like environment as possible in terms of capacity (memory, CPU), codebase (the actual build that will be deployed), and integrations with other environments or servers (if within test scope).&lt;/p&gt;
&lt;p&gt;Load testing is not linear: a response time of 5 seconds on a server with half the capacity of the production server does not necessarily equate to a response time of 2.5 seconds in production.&lt;/p&gt;
&lt;p&gt;This is also the time to think about your load injectors. Will they be on-premises, or in the cloud? A good entry criterion is the availability of the machines in the right network and with the right tools installed. If you’re using commercial tools, license provisioning should be a criterion. What sort of capacity will your load testing scripts require?&lt;/p&gt;
&lt;h3 id="support-are-there-people-available-with-expertise-in-key-components"&gt;Support: Are there people available with expertise in key components?&lt;/h3&gt;
&lt;p&gt;Load testing is a team activity. When a load test involves multiple application teams, it’s important to request availability of key persons on those teams during the test. Often as load testers, we are seen as working independently, but the truth couldn’t be farther from that. Load testing is a team sport. We need support from:&lt;/p&gt;
&lt;p&gt;business analysts who will be able to tell us how things are expected to work and what the current priorities are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;developers whom we can consult when poorly performing code needs to be optimised&lt;/li&gt;
&lt;li&gt;functional testers who can show us how the application works&lt;/li&gt;
&lt;li&gt;DevOps engineers who can help us provision and monitor servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and many more!&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-write-a-load-test-plan02.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="test-data-do-you-have-dummy-data-for-your-virtual-users-to-interact-with"&gt;Test Data: Do you have dummy data for your virtual users to interact with?&lt;/h3&gt;
&lt;p&gt;Once you have as close a copy of the production environment as possible, keep in mind that it’s still a clean copy, which may not be realistic. If there are databases in production, how much data do they contain? The application server may respond differently when your test database is empty compared to when it must contend with gigabytes of data in the production database.&lt;/p&gt;
&lt;p&gt;Using production data for testing purposes can be dangerous, not to mention illegal, in certain circumstances. To avoid this, you can scrub sensitive data or generate your own, either by injecting records into a database or by writing an automation script to create data on the front-end. If you write a script, you may be able to reuse parts of it later for your load test.&lt;/p&gt;
&lt;h2 id="workload-modeling"&gt;Workload Modeling&lt;/h2&gt;
&lt;p&gt;A workload model is a schema describing the load profile for a given test scenario, and it involves determining what (the key transactions), how much (the load distribution among the transactions) and when (timing of the load) to test.&lt;/p&gt;
&lt;p&gt;Workload modeling can be the most difficult part of the testing process because it involves finding out how load test scripting can best mimic what is actually happening in production. It can also be the most critical.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s my account of working on a project in horse racing, where workload modeling was of particular importance: &lt;a href="https://www.flood.io/blog/workload-modeling-preparing-for-large-events-like-the-melbourne-cup"&gt;Workload Modeling - Preparing for Large Events like the Melbourne Cup&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-write-a-load-test-plan03.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;When you&amp;rsquo;re selling bets for the Melbourne Cup, every second matters.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="server-monitoring"&gt;Server monitoring&lt;/h2&gt;
&lt;p&gt;Executing a load test without monitoring server health is like flying blind. You’ll know when you land safely and you’ll know when you crash, but even if you do crash, you won’t know why—or how you can avoid it next time. Monitoring server health is the black box that will tell you what went wrong.&lt;/p&gt;
&lt;p&gt;For load testing, you&amp;rsquo;ll want to monitor the application servers that you&amp;rsquo;re testing as well as the load generators that you&amp;rsquo;re using to run the load tests themselves. That&amp;rsquo;s right; if you&amp;rsquo;re not watching carefully, the machines you run the load tests on can be the bottlenecks in themselves, causing unnecessary failures in your load tests.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://testguild.com/performance-test-resource-utilization/"&gt;Joe Colantonio&lt;/a&gt; goes over the basics of resource utilization in terms of four main areas of concern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;Disk&lt;/li&gt;
&lt;li&gt;Network&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.flood.io/blog/load-testing-tutorial-server-monitoring"&gt;Here&amp;rsquo;s another guide&lt;/a&gt; I put together on the basic metrics as well as some tools you can use to set up your monitoring.&lt;/p&gt;
&lt;p&gt;Regardless of which metrics you identify as key or which tools you use, you&amp;rsquo;ll want to make sure that they&amp;rsquo;re all set up to measure server health before you run your load tests.&lt;/p&gt;
&lt;h2 id="test-doubles"&gt;Test doubles&lt;/h2&gt;
&lt;p&gt;Part of the strategic planning around load testing is deciding which components need to be tested in conjunction with your stated requirements. Applications can sometimes be complex enough that load testing end-to-end is not feasible due to the number of teams involved or the cost of duplicating application infrastructure.&lt;/p&gt;
&lt;p&gt;Keeping it as simple as possible will lead to fewer dependencies, so it&amp;rsquo;s worth considering creating test doubles to isolate relevant components. Test doubles include stubs, mocks, and full virtual services.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/how-to-write-a-load-test-plan04.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;A test double is that part that replaces a complicated component that is not within scope. It’s a “dumber” version that responds to requests enough to allow you to go on with your load testing without actually requiring that component.&lt;/p&gt;
&lt;p&gt;Test doubles allow us to abstract out other components we don&amp;rsquo;t want to test so that we can concentrate on the one that we do. The point of a test double is to remove the variables and noise introduced by other parts of the application so that we can focus on testing how one component responds.&lt;/p&gt;
&lt;p&gt;If you are willing to put the time into creating a test double, you can drastically reduce the amount of resources you need to set up an environment and isolate components. Reducing variables in your test allows you to more quickly determine where performance bottlenecks lie.&lt;/p&gt;
&lt;h2 id="test-scenarios"&gt;Test Scenarios&lt;/h2&gt;
&lt;p&gt;A test scenario is the description of a contained situation or condition under which the application will be tested. A test scenario is usually based on several test cases and includes a plan for how these test cases will be executed.&lt;/p&gt;
&lt;p&gt;Choosing your test scenarios means deciding which situation is most likely to yield the data that you require. Employing several different types of scenarios will give you a greater understanding of your application’s capabilities. You should feel free to create your own scenarios that are uniquely tailored to your requirements, but here are some common scenarios to start out with. Take the number of users and durations mentioned as guidelines and not rules.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what our Flooder amigo, Señor Performo, has to say about test scenarios:&lt;/p&gt;
&lt;p&gt;&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/EGzoAadzWwM?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;em&gt;Text version &lt;a href="https://www.flood.io/blog/ask-a-flooder-21-senor-performo-on-load-testing-scenarios"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Creating a test plan can seem daunting, but the process of writing it serves as a prompt for discussions among your team.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a &lt;a href="https://techbeacon.com/app-dev-testing/how-go-performance-tester-performance-engineer"&gt;pro tip from Scott Moore&lt;/a&gt;: Once you come up with a template you&amp;rsquo;re happy with, save it and reuse it for future projects. Modify it over time based on feedback to see progressive improvement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Building a set of reusable documents and other deliverables (&amp;ldquo;templates&amp;rdquo;) to use in the initial planning phase and final reporting will speed your initial startup time. I recommend working with or learning from others with experience to build out some of these templates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;‍When in doubt, remember that the format of a test plan is not as important as making sure it is meaningful and comprehensible for your team.&lt;/p&gt;
&lt;p&gt;‍For more information about the load testing process, check out &lt;a href="https://guides.flood.io/ebooks/api-load-testing"&gt;my book on API load testing&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Pros and Cons of API load testing</title><link>https://nicolevanderhoeven.com/blog/20200818-pros-and-cons/</link><pubDate>Tue, 18 Aug 2020 21:22:49 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200818-pros-and-cons/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/pros-and-cons-of-api-load-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/pros-and-cons-of-api-load-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;‍API load testing is one of the most popular ways to run a load test because of the number of open source and commercial load testing tools available on the market that support it. Creating a script to hit your application&amp;rsquo;s API endpoints is the traditional approach to load testing, but it&amp;rsquo;s not the only one.&lt;/p&gt;
&lt;p&gt;Here are some things to consider when deciding whether or not API load testing is right for your application.&lt;/p&gt;
&lt;h2 id="disadvantages-of-api-load-testing"&gt;Disadvantages of API load testing&lt;/h2&gt;
&lt;h3 id="api-load-testing-does-not-simulate-real-users-interacting-with-elements-of-your-webpage"&gt;API load testing does not simulate real users interacting with elements of your webpage.&lt;/h3&gt;
&lt;p&gt;‌In contrast to a customer opening up a browser and filling out forms on your site, an API load testing script will consist only of the underlying requests to the server that are made by clicking on those on-screen elements. Exactly how the customer triggers those requests, and what buttons they’ve pressed, is irrelevant. It’s all about the raw requests.‌&lt;/p&gt;
&lt;p&gt;The format of these requests will be dictated by the type of API your application uses, and it may take some technical knowledge or expertise to be able to formulate requests correctly. However, a RESTful API is one of the most common types, and it is considered to be relatively simple to use.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200818-01.png" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;HTTPS requests for amazon.com. This is what an API load testing script would need to replicate.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="it-doesnt-give-you-an-idea-of-how-user-friendly-your-application-is"&gt;It doesn’t give you an idea of how user-friendly your application is.&lt;/h3&gt;
&lt;p&gt;‌API load testing tools don’t give you feedback on how long your pictures took to render on your users’ browsers or whether that “SUBMIT” button is in an obvious spot.&lt;/p&gt;
&lt;h3 id="it-doesnt-measure-front-end-performance-or-how-quickly-pages-render-in-different-browsers"&gt;‌It doesn’t measure front-end performance or how quickly pages render in different browsers.&lt;/h3&gt;
&lt;p&gt;‌While all the resources that the server returns can be downloaded by your tool of choice, there is no browser on which to run them.&lt;/p&gt;
&lt;h3 id="api-load-testing-doesnt-run-client-side-scripts"&gt;‌API load testing doesn’t run client-side scripts.&lt;/h3&gt;
&lt;p&gt;‌It isn’t what you’re looking for if your application is a single-page web app that relies heavily on JavaScript or AJAX to dynamically populate and update the page. Your load testing tool will download the scripts, but not execute them.‌&lt;/p&gt;
&lt;p&gt;‌This includes scripts like those required to trigger Google Analytics, which prompts many an engineer to question the results of an API load test due to the inability to see the traffic come in on Google Analytics. API load testing won’t help you with that. Running browser-level load tests using tools like &lt;a href="https://element.flood.io"&gt;Flood Element&lt;/a&gt; or &lt;a href="https://selenium.dev"&gt;Selenium&lt;/a&gt; may be more useful.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve decided that API load testing is not the best way to load test your application, you can look at browser-based load testing as an alternative.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200818-02.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Executing a browser-based load testing script using Flood Element.&lt;/em&gt;
‌&lt;/p&gt;
&lt;h2 id="advantages-of-api-load-testing"&gt;‌Advantages of API load testing&lt;/h2&gt;
&lt;p&gt;Now that we’ve talked about what API load testing isn’t good at, let’s talk about what it IS good at.&lt;/p&gt;
&lt;p&gt;‌### It allows you to load test specific servers rather than the whole stack.&lt;/p&gt;
&lt;p&gt;‌API load testing allows you to tailor your load testing by applying load only on particular servers. This is particularly useful in more complicated applications that involve several components and would require substantial effort to reproduce in a test environment. Using APIs, you can test only the functions you want to test.&lt;/p&gt;
&lt;h3 id="it-is-well-supported-"&gt;It is well-supported. ‌&lt;/h3&gt;
&lt;p&gt;API load testing has been around for decades. There are lots of robust tools you can choose from, both commercial and open-source, and many of these tools have large communities and extensive documentation around how to script the most common cases. It’s a far cry from the browser-level testing space, which is relatively new and sparsely populated by comparison.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200818-03.png" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;The &lt;a href="https://jmeter.apache.org/usermanual/index.html"&gt;JMeter site&lt;/a&gt; contains a wealth of information, including a User&amp;rsquo;s Manual&lt;/em&gt;
‍&lt;/p&gt;
&lt;h3 id="its-not-as-resource-intensive-as-browser-level-testing"&gt;It’s not as resource-intensive as browser-level testing.&lt;/h3&gt;
&lt;p&gt;You can simulate more users with API load testing than with browser-level load testing. Since most interactions on the browser-level get translated to requests anyway, generating load this way is incredibly efficient, allowing you to hit your server with requests while bypassing the UI layer and the resource overhead that that entails.&lt;/p&gt;
&lt;p&gt;For example, at &lt;a href="https://flood.io"&gt;Flood&lt;/a&gt; we’ve baselined some of the tools that we support to see how many users we can run on an AWS m5.xlarge instance:&lt;/p&gt;
&lt;p&gt;Selenium: 5 users
Flood Element: 20-40 users
JMeter: 1000 users
Gatling: 1000 users&lt;/p&gt;
&lt;p&gt;‌You’ll see that the browser-level test tools, Selenium and Flood Element, can run significantly fewer users on the same sized node as can the protocol-level tools, JMeter and Gatling. This isn’t due to an inefficiency in the tools but rather to the fact that browser-level tools inherently need more CPU and memory to start separate instances of browsers per user and render pages graphically. You can run more users per node (or machine) by carrying out API load testing with protocol-level tools.&lt;/p&gt;
&lt;h3 id="its-cheaper"&gt;It’s cheaper.&lt;/h3&gt;
&lt;p&gt;‌The efficiency in resource utilization translates directly into cost savings because every node that you don’t need to execute your tests on is one fewer node that you’ll have to pay to provision (whether on premises or in the cloud).&lt;/p&gt;
&lt;p&gt;‌For this reason, API load testing is one of the most cost-efficient ways you can get started with load testing, allowing you to scale up your load relatively cheaply while getting immediate results.&lt;/p&gt;
&lt;h2 id="how-to-get-started-with-api-load-testing"&gt;How to get started with API load testing&lt;/h2&gt;
&lt;p&gt;API load testing isn&amp;rsquo;t for every application, but depending on your test scenario, it may be the easiest way to test application performance.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re new to non-functional testing or API load testing in particular and would like some step-by-step tutorials on how to get started, check out my free ebook, &lt;a href="https://guides.flood.io/ebooks/api-load-testing"&gt;API Load Testing: A Beginner&amp;rsquo;s Guide&lt;/a&gt;. It takes you through the basics of API load testing, including planning your first load test, selecting an open source tool, scripting, executing a test on the cloud, analyzing results, and even integrating it into your CI/CD pipelines.&lt;/p&gt;</description></item><item><title>Performance testing the Dutch coronavirus hotline</title><link>https://nicolevanderhoeven.com/blog/20200813-dutch-coronavirus-hotline/</link><pubDate>Thu, 13 Aug 2020 22:18:04 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200813-dutch-coronavirus-hotline/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/performance-testing-the-story-of-the-dutch-coronavirus-hotline"&gt;
&lt;p&gt;&lt;em&gt;Read the original Dutch version &lt;a href="https://nicolevanderhoeven.com/blog/20200704-performanceverbeteringen-voor-de-coronalijn/"&gt;here&lt;/a&gt;.&lt;/em&gt;
&lt;em&gt;This English version was first posted &lt;a href="https://www.flood.io/blog/performance-testing-the-story-of-the-dutch-coronavirus-hotline"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On June 1st, 2020, the National Institute of Health and Environment (RIVM) announced that everyone with certain symptoms could call the hotline 0800-1202 to arrange a free COVID-19 test. The promise: call today, get tested tomorrow, and receive the results the day after that. The reality, though, did not live up to expectations.&lt;/p&gt;
&lt;p&gt;The opening day of the hotline was chaotic, and many Dutch people reported connection problems due to a system overload. Yet this could have been prevented with a better understanding of how human psychology can affect application performance.&lt;/p&gt;
&lt;h2 id="the-situation"&gt;The situation&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://nos.nl/collectie/13824/artikel/2335758-ggd-en-klaar-voor-massaal-testen-tijd-van-limiterende-factoren-voorbij"&gt;According to Dr. Ann Vossen&lt;/a&gt;, chairperson of the task force charged with scaling up mass testing in the Netherlands, the country was ready to process 30,000 tests per day. This number corresponds to &lt;a href="https://nos.nl/artikel/2335790-drukte-op-nieuw-telefoonnummer-voor-coronatests-0800-1202.html"&gt;the national capacity&lt;/a&gt; of all the municipal health clinics (GGDs) in the Netherlands. Despite this claim, though, &lt;a href="https://www.ad.nl/binnenland/liefst-323-000-telefoontjes-naar-nieuw-nummer-voor-coronatest-systemen-overbelast~a16a9a50"&gt;only 1,146 people were actually tested&lt;/a&gt; on the first day of testing (the day after the opening of the hotline). Perhaps more importantly, only 5,748 appointments were set on June 1st. The situation was no doubt exacerbated by the overwhelming 323,000 people who called the hotline on its first day, prompting even the telephone operators to admit that &lt;a href="https://www.volkskrant.nl/nieuws-achtergrond/coronatestnummer-kampt-met-kinderziektes-maar-ggd-s-kunnen-de-drukte-goed-aan~b2f332d2/"&gt;they&amp;rsquo;d had troubles&lt;/a&gt; logging into the appointment system.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Bron: From &lt;a href="https://twitter.com/GGDGHORNL/status/1267351996379643906"&gt;@ggdghornl&lt;/a&gt;, Twitter&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[&amp;ldquo;We see that there are many calls going to 0800-1202. You can call for an appointment until 8pm tonight. Please try your call later to avoid long wait times.&amp;rdquo;]&lt;/p&gt;
&lt;p&gt;On Twitter, users shared horror stories: one claimed to have &lt;a href="https://twitter.com/Ed_van_Iterson/status/1267429180347691011"&gt;waited on the line for 6 hours&lt;/a&gt;, only to be told that the system was down and no appointments could be made; another complained about &lt;a href="https://twitter.com/maticus_spazz/status/1267374692941418496"&gt;getting disconnected&lt;/a&gt; after getting in touch with an operator. Yet another &lt;a href="https://twitter.com/leovanlinden/status/1267408142813605888"&gt;posted a video&lt;/a&gt; of calling the hotline and a recorded message saying &amp;ldquo;Al onze medewekers zijn op dit moment in gesprek. We zullen u zo snel mogelijk te woord staan. [All of our operators are currently on other calls. We will answer you as soon as possible].&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The second day was somewhat better: &lt;a href="https://www.volkskrant.nl/nieuws-achtergrond/coronatestnummer-kampt-met-kinderziektes-maar-ggd-s-kunnen-de-drukte-goed-aan~b2f332d2/"&gt;there were 11,000 appointments made&lt;/a&gt;, partly because of the lower amount of phone calls and partly because of some fixes of technical problems. In the first week, the number of appointments increased to 50,000 (about 7,142 per day), which was still significantly lower than expected.&lt;/p&gt;
&lt;p&gt;There are some lessons that we can learn from this case study.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-02.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: Coronavirus test procedure from &lt;a href="https://ggdhm.nl/thema-s/item/infectieziekten/testbeleid"&gt;GGD Hollands Midden&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;English translation:&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do you have these (mild) symptoms?:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cough&lt;/li&gt;
&lt;li&gt;blocked or runny nose&lt;/li&gt;
&lt;li&gt;fever&lt;/li&gt;
&lt;li&gt;loss of smell or taste&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Step 1: Make an appointment&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Call the national number 0800-1202&lt;/li&gt;
&lt;li&gt;Have your BSN with you when you call&lt;/li&gt;
&lt;li&gt;Make an appointment to get tested at one of our locations&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Step 2: Test&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to the selected location at the scheduled time&lt;/li&gt;
&lt;li&gt;You will be tested at the test location&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Step 3: Results and eventual start of source and contact research&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You will be called with your results within 48 hours&lt;/li&gt;
&lt;li&gt;If you are infected with the coronavirus, the GGD will call you for source and contact research
*If you show more serious symptoms, or if you fall within a high-risk group, report to your doctor or to the emergency help line.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="the-effect-of-fear-on-performance"&gt;The effect of fear on performance&lt;/h2&gt;
&lt;p&gt;This coronavirus has already shown us a side of humanity that we&amp;rsquo;d perhaps rather not see: &lt;a href="https://www.bbc.com/news/world-australia-51731422"&gt;fights over toilet paper&lt;/a&gt;, &lt;a href="https://snacks.robinhood.com/newsletters/rMxwcb7y7lMZoI5NY6qf7/"&gt;chaos on the stock market&lt;/a&gt; due to panic, and &lt;a href="https://www.volkskrant.nl/nieuws-achtergrond/nctv-waarschuwt-voor-extremistische-protestacties-door-5g-tegenstanders-die-vrezen-voor-coronavirus~bf1400b8/"&gt;sabotage of 5G cell towers&lt;/a&gt; based on a belief that they caused COVID-19. As much as we&amp;rsquo;d like to believe it, we&amp;rsquo;re not always the most rational beings. We all have the tendency to behave unpredictably, especially when we&amp;rsquo;re afraid.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s no wonder that the coronavirus testing line was so welcome. The fact that there were 323,000 callers on the first day is even more remarkable when we remember that majority of Dutch residents had been in self-isolation for months at that point, which lowered their chances of contracting the virus. In theory, the fear that drove these callers might have been somewhat irrational, but not unpredictable.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-03.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;In English, we call this FOMO (Fear of Missing Out). We&amp;rsquo;ve already clearly seen &lt;a href="https://www.flood.io/blog/fomo-and-performance-testing-why-robinhood-went-down"&gt;how FOMO can drive users&lt;/a&gt; en masse to an application, with effects on its performance. In this case, people were afraid that coronavirus test shortages would mean there weren&amp;rsquo;t enough tests to go around, and they wanted to be first in line to get tested.&lt;/p&gt;
&lt;p&gt;Another problem was that &lt;a href="https://www.trouw.nl/binnenland/de-balans-na-een-week-testen-het-plan-was-mooier-dan-de-praktijk~b86399b7/"&gt;one in four callers&lt;/a&gt; just wanted general information about COVID-19, despite government pleas to keep the hotline free for coronavirus test appointments only. It didn&amp;rsquo;t help that the hotline opened on a Dutch national holiday, White Monday, and more people were home than usual for a Monday. The high number of unrelated calls was unexpected, but nevertheless factored into system overload.&lt;/p&gt;
&lt;p&gt;What could we have done to improve this system?&lt;/p&gt;
&lt;h2 id="improvements-to-the-corona-hotline"&gt;Improvements to the corona hotline&lt;/h2&gt;
&lt;h3 id="better-estimation"&gt;Better estimation&lt;/h3&gt;
&lt;p&gt;As difficult as it is to predict FOMO&amp;rsquo;s effects, we can still try. We can make educated guesses based on related statistics.&lt;/p&gt;
&lt;p&gt;Just one month before the coronavirus test hotline was opened, &lt;a href="https://www.zwollenu.nl/500-000-tot-700-000-nederlanders-besmet-met-coronavirus/"&gt;Jaap van Dissel&lt;/a&gt;, the head of RIVM, estimated that there were up to 700,000 Dutch residents infected by the coronavirus. Why, then, was it such a surprise that more than 300,000 of them had called? Furthermore, the RIVM had encouraged people with a broad list of symptoms (such as colds, coughs, or fever) to call, and it&amp;rsquo;s reasonable to assume that some people with conditions other than COVID-19 might also have called.&lt;/p&gt;
&lt;p&gt;Even if we assume that 20% of the 700,000 people with the novel coronavirus did not have symptoms (&lt;a href="https://www.who.int/docs/default-source/coronaviruse/situation-reports/20200306-sitrep-46-covid-19.pdf?sfvrsn=96b04adf_4"&gt;according to WHO research&lt;/a&gt;), we still arrive at a predicted 140,000 calls, which is still 25 times what was the coronavirus testing system was able to process on the first day.&lt;/p&gt;
&lt;p&gt;If we have an educated estimate, we can start testing.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200704-04.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="injecting-load"&gt;Injecting load&lt;/h3&gt;
&lt;p&gt;When a resident calls the hotline, the call is routed to one of several operators working from home. This is often done with a telephony server that forwards calls based on some type of round-robin system. We can execute load tests on this server to determine how many phone calls a server can process and route successfully, with minimal delay and without message queueing.&lt;/p&gt;
&lt;p&gt;Then, the operator enters personal information from the caller into the appointment system, including their government identification number (BSN) and address. We can surmise that the system checks the identity to verify the validity of the BSN, and then sends some details to a database. The closest municipal clinic to the caller can then use the data to process and schedule an appointment. We can also inject load at this stage by simulating the calls that the appointment system creates with an API load testing tool like JMeter or Gatling.&lt;/p&gt;
&lt;h3 id="using-different-test-scenarios"&gt;Using different test scenarios&lt;/h3&gt;
&lt;p&gt;With such a public announcement of the hotline&amp;rsquo;s opening, a &lt;strong&gt;spike test&lt;/strong&gt; before release could have yielded some useful information. In a spike test, we simulate a sharp increase in users over a short period of time. For example, we could have simulated an increase in users from 0 to 700,000 on the telephony system over 10 minutes to see how the application would have handled that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Soak tests&lt;/strong&gt; could also have been useful in this case. The hotline&amp;rsquo;s opening hours were from 8 am to 8 pm, so it was open for 12 hours in total. With soak tests, we can generate load on an application for an extended amount of time. Soak tests can reveal memory leaks or other bottlenecks in the processing of data that might occur after hours.&lt;/p&gt;
&lt;p&gt;These are ways we could have tested the system as it is, but are there improvements that we could have made in the system to make it more resilient to reduce the FOMO factor?&lt;/p&gt;
&lt;h2 id="using-automation-to-lessen-fomo-risk"&gt;Using automation to lessen FOMO risk&lt;/h2&gt;
&lt;p&gt;The system had some manual parts of the process that may have exacerbated load issues. Here&amp;rsquo;s how this system could have been automated to reduce bottlenecks.&lt;/p&gt;
&lt;h3 id="interactive-voice-response"&gt;Interactive Voice Response&lt;/h3&gt;
&lt;p&gt;Telephony servers commonly use Interactive Voice Response (IVR), an automated menu system for voice calls that uses either voice recognition or keypress tones to navigate a user through a series of choices. We&amp;rsquo;re all familiar with it: &amp;ldquo;&lt;em&gt;For Dutch, press 1. For English, press 2&amp;hellip;&lt;/em&gt;&amp;rdquo; Such a system might have helped reduce the overall load by quickly filtering out callers in the 25% who weren&amp;rsquo;t interested in creating an appointment. &amp;ldquo;&lt;em&gt;If you&amp;rsquo;d like to book an appointment for a coronavirus test, press 1. Otherwise, please call the information hotline at&amp;hellip;.&lt;/em&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what an IVR system could look like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-05.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;From: &lt;a href="http://vanguardnetworks.com.sg/IVR.html"&gt;Vanguard Networks&lt;/a&gt;&lt;/em&gt;
‍
Load testing IVR systems can be difficult, and it usually requires the use of highly specialized commercial tools. However, introducing a new tool may also increase the time required for load testing.&lt;/p&gt;
&lt;h3 id="building-a-web-app"&gt;Building a web app&lt;/h3&gt;
&lt;p&gt;Another approach would be to implement a web app to verify identity and capture personal information quickly. In the Netherlands, we already have &lt;a href="https://www.digid.nl/wat-is-digid/"&gt;DigiD&lt;/a&gt;, an identity verification system that is heavily used for governmental services. &lt;a href="https://mijn.overheid.nl/"&gt;Mijn Overheid&lt;/a&gt;, which is a central government portal accessible to every Dutch resident, already interfaces with DigiD as the sole method of logging in. Reusing these existing services could have saved a lot of the work on a web app— not to mention reducing the bottlenecks inherent in a more manual process like a telephone hotline.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200704-06.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: &lt;a href="https://mijn.overheid.nl/"&gt;Mijn Overheid&lt;/a&gt;&lt;/em&gt;
‍&lt;/p&gt;
&lt;h2 id="testing-the-system-end-to-end"&gt;Testing the system end-to-end&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ve identified a few ways to load test various components of the coronavirus hotline, both in its current incarnation as well as in hypothetical improved versions. Isolating each component and running load tests at that level can help us resolve performance issues inherent in the component. However, there are still bottlenecks that are only revealed when the integrations between components is tested as well. That&amp;rsquo;s where end-to-end testing comes in.&lt;/p&gt;
&lt;p&gt;For the coronavirus hotline, end-to-end testing means being able to run load tests on the entire process and seeing how data flows from one to the other: the call forwarding from the telephony server, the identity verification system, the appointment system, the database of personal information, the servers of every municipal clinic, and the email/text notifications with appointment details. As modern applications grow more complex, it can be tempting not to run end-to-end tests, but we do so at great risk of functional or nonfunctional issues.&lt;/p&gt;
&lt;p&gt;An area of end-to-end testing that is often overlooked is the human factor, which can also be a bottleneck. Had hotline operators been trained to ask early on in the call whether the caller wanted to make an appointment, to filter out unrelated calls? Can the coronavirus testing and test result processing, heavily involving manual work from medical professionals, match the stated requirements (test within 24 hours, result within 48 hours) even when the digital components are performant? Some people waited for an hour in the cities of &lt;a href="https://www.ed.nl/eindhoven/in-de-file-voor-een-stukje-zekerheid-meteen-drukte-bij-teststraat-voor-corona-in-eindhoven~a27eec83/"&gt;Eindhoven&lt;/a&gt; and &lt;a href="https://www.pzc.nl/zeeuws-nieuws/drukte-bij-teststraat-in-goes-na-openstellen-nummer-voor-coronatest~aa8a6cb2/"&gt;Goes&lt;/a&gt; due to traffic congestion from cars near the test centers, leading to delays in the testing schedule.&lt;/p&gt;
&lt;p&gt;While it can be difficult to load test these logistical systems with automated tools, they may have significant effects on the overall performance of an application and should be considered.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Often, performance issues can lead to a tangible loss in profits or a more intangible loss in reputation. In this case study, poor performance had a direct impact on people&amp;rsquo;s health. Potentially sick people were unable to make appointments to get tested, and later treated. Delayed tests could have led to an increased infection rate as people waited for a confirmed diagnosis. Hotline operators also admitted that slowness in the system led to them sometimes &lt;a href="https://wnl.tv/2020/06/08/chaos-bij-de-coronalijn-personeel-slaat-alarm/"&gt;giving out test results&lt;/a&gt; against instructions to wait for trained medical professionals to do so.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200704-07.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Human psychological factors can make a system more complex to load test. &lt;a href="https://www.trouw.nl/binnenland/de-balans-na-een-week-testen-het-plan-was-mooier-dan-de-praktijk~b86399b7/"&gt;According to Andrea Evers&lt;/a&gt; (LUCM), a health psychological professor, &amp;ldquo;&lt;em&gt;Door de uitbraak van corona zijn mensen in een langdurige stresssituatie beland. Onzekerheid, onvoorspelbaarheid en oncontroleerbaarheid maken dat ze de behoefte hebben om zaken juist wel te controleren of voorspelbaar te maken&lt;/em&gt; [The coronavirus outbreak has put people in a situation of chronic stress. The uncertainty, unpredictability, and helplessness of the situation cause a need to make things controllable or predictable].&amp;rdquo;&lt;/p&gt;
&lt;p&gt;As performance testers, we can use statistics and a systematic attitude combined with knowledge of human factors in applications to try to predict the unpredictable.&lt;/p&gt;</description></item><item><title>Ask a Flooder 20: How can fear affect application performance?</title><link>https://nicolevanderhoeven.com/blog/20200806-aaf20/</link><pubDate>Thu, 06 Aug 2020 22:43:28 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200806-aaf20/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-20-how-can-fear-affect-application-performance-video"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-20-how-can-fear-affect-application-performance-video"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this video, I talk about the power of fear, and how it can end up affecting performance test results.
‍&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/oeEFCrTYULE?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Or listen to the audio version here:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/2cOfQrrUwyIKWbztgNdoXR" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;p&gt;Hi everyone, I&amp;rsquo;m Nicole van der Hoeven, back with another Ask a Flooder, and today I want to talk about the effect of fear on your performance outcomes.&lt;/p&gt;
&lt;h2 id="the-dutch-coronalijn"&gt;The Dutch &amp;ldquo;coronalijn&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;I currently live in Maastricht, in the Netherlands. At the beginning of this global pandemic, the only people getting tested actively for COVID-19 were either people who presented with serious symptoms of the disease, or health care professionals who were continually exposed to the disease and therefore at a higher risk of contracting it.&lt;/p&gt;
&lt;p&gt;Last month, on June 1st, the Dutch government made testing available to everybody. They set up a telephone hotline that any Dutch resident could call to set an appointment to get tested for a COVID-19 test. A task force was dispatched to make sure that they could test up to 30.000 people per day. Yet on the first day of testing, only 1.146 people were tested. So what happened?&lt;/p&gt;
&lt;p&gt;Well, it turned out that the bottleneck of the entire process wasn&amp;rsquo;t getting enough tests, registering the tests, or even processing the test results. The bottleneck actually occurred way before that, when people called up the hotline to get an appointment.&lt;/p&gt;
&lt;p&gt;323.000 people called on the first day that the hotline opened. And not even all of them had called to make an appointment. Some of them were just calling for general information on the coronavirus despite the fact that the government made it clear that that&amp;rsquo;s not what the line was for, and that there were other lines dedicated to information. There were reports on Twitter of people waiting up to 6 hours on the line just to talk to someone who would make an appointment for them. There were calls that got dropped. People said that sometimes it happened in the middle of a call. Even telephone operators that the appointment system was down or slow and unusable for a lot of it.&lt;/p&gt;
&lt;p&gt;As a result, only 5,500 people actually had appointments booked on a day where there were 323,000 that called. That&amp;rsquo;s 1.70%.&lt;/p&gt;
&lt;h2 id="what-does-that-have-to-do-with-performance"&gt;What does that have to do with performance?&lt;/h2&gt;
&lt;p&gt;As performance engineers, we like to believe that everything we do is reasoned. We design tests based on quantitative data. We look at historical trends and metrics to help us decide how to build a workload model. But the truth is that that&amp;rsquo;s often not enough.&lt;/p&gt;
&lt;p&gt;If anything, this pandemic has shown us just how wrong our guesses can be, whether or not they&amp;rsquo;re informed by facts, especially when it has to do with human behavior. We&amp;rsquo;ve seen violent fights erupt over toilet paper. We&amp;rsquo;ve seen peaks and troughs on the stock market that didn&amp;rsquo;t have anything to do with the underlying companies. We&amp;rsquo;ve seen arson on 5G cell towers. Because human beings are irrational, and it&amp;rsquo;s really hard to capture that irrationality in our model for how users are going to behave.&lt;/p&gt;
&lt;p&gt;We may never be able to accurate predict how irrationality is going to affect the load on our application in production. But that doesn&amp;rsquo;t mean that we shouldn&amp;rsquo;t try. There are still things that we can do to mitigate the risk. I think that any application that is public-facing should be assessed for exposure to the risk of fear or other human psychological factors.&lt;/p&gt;
&lt;h2 id="how-do-we-account-for-fear-in-our-performance-testing"&gt;How do we account for fear in our performance testing?&lt;/h2&gt;
&lt;p&gt;I actually wrote a whole blog post on this particular situation (the Dutch corona line), and how it might have been possible to predict (maybe not with 100% accuracy but with reasonable accuracy) how many people would have called the hotline.&lt;/p&gt;
&lt;p&gt;For example, we can look at statistics like the of the Netherlands or how many people normally get flu-like symptoms or at least report them at this time of the year. These are statistics that are readily available from the Department of Health, and we could have used them to inform our decisions on the workload model for such an application. We may not have gotten it exactly, but we would have at least bin in the same ballpark.&lt;/p&gt;
&lt;p&gt;If we have a good enough idea about the workload that our application would have to handle when it&amp;rsquo;s influenced by fear, we can start running load tests around that. Another good open-ended kind of test to run, especially for applications that are particularly susceptible to fear, is the stress test. In a stress test, you push your application to its limits and you find out what they are. You could, for instance, start with your peak work load, and then add a certain number of users every 30 minutes and see how much your application can actually handle. You can do the same with soak tests, where you&amp;rsquo;re exposing your application to the same amount of load for an extended period of time. Both of those kind of exploratory tests help you define the limits of your application and how it would respond if fear plays a role in the workload model in production.&lt;/p&gt;
&lt;p&gt;If we overlook this critical human element in our test design, then we leave ourselves, and our applications, vulnerable to performance degradation and, perhaps, outages.&lt;/p&gt;
&lt;p&gt;Till next time, happy flooding!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Read &lt;a href="https://nicolevanderhoeven.com/blog/20200813-dutch-coronavirus-hotline/"&gt;this blog post&lt;/a&gt; for more about the coronavirus hotline.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Ask a Flooder 18: How do I load test video streaming with JMeter?</title><link>https://nicolevanderhoeven.com/blog/20200722-aaf18/</link><pubDate>Wed, 22 Jul 2020 22:49:15 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200722-aaf18/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-18-how-do-i-load-test-video-streaming-with-jmeter-video"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-18-how-do-i-load-test-video-streaming-with-jmeter-video"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this video, I answer the question: &amp;ldquo;How do I load test video streaming with JMeter?&amp;rdquo;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Q00briuogto?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Or listen to the audio version here:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/69F5lmOeFfEJr6ZgVWFGOu" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="http-live-streaming-hls-and-performance"&gt;HTTP Live Streaming (HLS) and performance&lt;/h2&gt;
&lt;p&gt;One of the most popular protocols for video streaming is &lt;a href="https://developer.apple.com/streaming/examples/"&gt;HTTP Live Streaming&lt;/a&gt;, or HLS, and that&amp;rsquo;s what I&amp;rsquo;m going to show you today. An HLS video ends with the [file extension .m3u8‍](&lt;a href="https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8%5D(https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8)"&gt;https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8](https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8)&lt;/a&gt;. According to the HLS protocol, when a video is streamed, the first request sent must be a manifest. A manifest is sometimes called a playlist, and it is a list of all the chunks or parts that make up the video. Then, each chunk is downloaded.&lt;/p&gt;
&lt;p&gt;To assess the performance of a video stream, we still want the response time of the manifest, the chunks, and the whole video. However, there are many more metrics that are important in video streaming which aren&amp;rsquo;t captured by just the response time. For example, how do you determine whether a video was played smoothly or if it stopped to buffer? A video that keeps pausing might just drive a user away, and it&amp;rsquo;s difficult to tell whether that is happening just from the response time.&lt;/p&gt;
&lt;h2 id="how-to-load-test-video-streaming-with-jmeter"&gt;How to load test video streaming with JMeter&lt;/h2&gt;
&lt;h3 id="downloading-and-installing-the-ubik-video-streaming-plugin"&gt;Downloading and installing the Ubik Video Streaming plugin&lt;/h3&gt;
&lt;p&gt;Luckily, there&amp;rsquo;s a plugin for that, created by a company called Ubik Ingénierie. The &lt;a href="https://ubikloadpack.com/"&gt;Ubik Load Pack&lt;/a&gt; is a set of enterprise-grade plugins for JMeter. One of those plugins is a &lt;a href="https://ubikloadpack.com/video-streaming-performance.php"&gt;Video Streaming Plugin&lt;/a&gt;, and it happens to be a really easy way to get started with video streaming. This is a paid plugin, but you can &lt;a href="https://ubikloadpack.com/video-streaming-performance.php#formulaire"&gt;sign up for a free 11-day trial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you do that, you&amp;rsquo;ll get an email from Ubik with a link to download the bundle. Go to lib/ext and then copy this .jar file into the same directory on your JMeter installation. Start up JMeter, and it&amp;rsquo;ll ask you for the license file. Browse to the license file in the bundle you downloaded. JMeter will restart.&lt;/p&gt;
&lt;h3 id="using-the-jmeter-plugin-in-your-load-testing-script"&gt;Using the JMeter plugin in your load testing script&lt;/h3&gt;
&lt;p&gt;Now let&amp;rsquo;s start creating the Test Plan. Create a Thread group, and then right click on the thread group, go to Add &amp;gt; Sampler and then select the &amp;ldquo;ULP Video Streaming sampler&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Click on the sampler and then paste in the URL to the HLS file that you&amp;rsquo;d like to test. This should end in a .m3u8. This plugin supports several protocols for video streaming, but we can just leave it at auto and it&amp;rsquo;ll automatically detect which one it is.&lt;/p&gt;
&lt;p&gt;In Play Options, you can choose whether to play the entire video or just a part of it, or even whether you want to include the video subtitles in your test.&lt;/p&gt;
&lt;p&gt;This Network Options panel is fantastic because it allows you to limit your network speed. Slow networks, like mobile networks, really affect user experience, so it&amp;rsquo;s really important to take this into account when running load tests. You might even use a parameter here and do something like throttle to 3G speeds for 80% of your traffic, and leave it on Auto 20% of the time. But I&amp;rsquo;ll just leave this blank for now.&lt;/p&gt;
&lt;p&gt;Next, let&amp;rsquo;s add a View Results Tree listener. Then, let&amp;rsquo;s run the script once, just to see if it works.&lt;/p&gt;
&lt;p&gt;As you can see, we can see the manifest retrieved first, and then all the chunks. We didn&amp;rsquo;t get any errors, so let&amp;rsquo;s run this as a load test. We can disable this listener because it just consumes extra resources on our load generator.&lt;/p&gt;
&lt;h3 id="ramping-up-your-load-test-on-flood"&gt;Ramping up your load test on Flood&lt;/h3&gt;
&lt;p&gt;To ramp this up into a load test, we&amp;rsquo;ll have to go into Thread Group and change these settings. Ideally, we want to be able to change these values from the Flood UI later so we don&amp;rsquo;t have to go into the script whenever we change them, so I&amp;rsquo;ll type in these Flood-specific properties. I&amp;rsquo;ll leave a link in the description about these properties. I&amp;rsquo;ll also set it to run Forever so that we can change the duration and the video will just keep playing to fill that duration.&lt;/p&gt;
&lt;p&gt;To capture some of Ubik&amp;rsquo;s cool metrics, let&amp;rsquo;s add a Simple Data Writer listener here and write the results to a CSV file. On Flood, all flood results are stored on /data/flood/results, so we&amp;rsquo;ll add an output.csv file in that directory that we can download after the test.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s our script! Now we&amp;rsquo;ll have to include the plugin and related files so that Flood can access them as well.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s go back to the bundle we downloaded and go to the bin folder to look for &lt;a href="http://user.properties/"&gt;user.properties&lt;/a&gt;. Let&amp;rsquo;s open that up, and we&amp;rsquo;ll change the license path to &lt;code&gt;/data/flood/files&lt;/code&gt;, which is the directory that Flood saves files we upload to. To preserve this directory structure, we&amp;rsquo;ll compress just the lib and bin folders.&lt;/p&gt;
&lt;p&gt;On Flood, we&amp;rsquo;ll start a new stream here and upload three things: the jmx script, the license, and the compressed archive of the lib and bin folders from the bundle.&lt;/p&gt;
&lt;p&gt;Then we&amp;rsquo;ll go through the rest of the process here, setting the test parameters. Time to launch the test!&lt;/p&gt;
&lt;h3 id="analyzing-video-streaming-load-test-results"&gt;Analyzing video streaming load test results&lt;/h3&gt;
&lt;p&gt;Flood shows you results of your load test while it&amp;rsquo;s running as well as after the test. You can see metrics like concurrency, network throughput, transaction rate, and response time for the test. You can also see these metrics for each individual transaction, including the video or sampler as a whole, the manifest, and the chunks.&lt;/p&gt;
&lt;p&gt;To see the CSV that our script generated, we can download the Archived Results from a flood.&lt;/p&gt;
&lt;p&gt;In JMeter, click on Tools &amp;gt; Generate Report and fill in the fields, selecting the output.csv from the flood&amp;rsquo;s Archived Results, JMeter&amp;rsquo;s &lt;a href="http://user.properties"&gt;user.properties&lt;/a&gt; file, and then an output directory for the report.&lt;/p&gt;
&lt;p&gt;Ubik&amp;rsquo;s plugin creates a few custom graphs in the HTML report:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Buffer fill in Millis&lt;/li&gt;
&lt;li&gt;Lag ratio in percentage&lt;/li&gt;
&lt;li&gt;Lag ratio without buffer fill in percentage&lt;/li&gt;
&lt;li&gt;Lag time in Millis&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Using Ubik&amp;rsquo;s Video Streaming plugin is an easy and thorough way to load test video streaming with JMeter, and I highly recommend it. I used an HLS video to demonstrate it, but this plugin supports several protocols:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HLS&lt;/li&gt;
&lt;li&gt;Mpeg DASH&lt;/li&gt;
&lt;li&gt;Smooth&lt;/li&gt;
&lt;li&gt;Adobe HDS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information, read Ubik&amp;rsquo;s tutorial on how to use their plugin with Flood &lt;a href="https://www.flood.io/blog/running-a-video-streaming-performance-test-using-flood-io-and-ubikloadpack"&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Performanceverbeteringen voor de coronalijn: de kracht van angst</title><link>https://nicolevanderhoeven.com/blog/20200704-performanceverbeteringen-voor-de-coronalijn/</link><pubDate>Sat, 04 Jul 2020 21:35:43 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200704-performanceverbeteringen-voor-de-coronalijn/</guid><description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@n_vanderhoeven/performanceverbeteringen-voor-de-coronalijn-de-kracht-van-angst-8c3713a3b7e0"&gt;Medium&lt;/a&gt;. English version &lt;a href="https://www.flood.io/blog/performance-testing-the-story-of-the-dutch-co2020ronavirus-hotline"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Op 1 juni heeft de landelijke GGD Ghor aangekondigd dat iedereen met klachten een coronatest zou kunnen aanvragen door de afsprakenlijn (0800–1202) te bellen. De belofte: vandaag het telefoongesprek, morgen de coronatest en over twee dagen de uitslag. Echter was het in werkelijkheid niet zo makkelijk. Op de eerste dag was er chaos en veel Nederlanders meldden verbindingsproblemen vanwege drukte op de overbelaste systemen.&lt;/p&gt;
&lt;p&gt;Dit had voorkomen kunnen worden door een beter begrip te hebben over de manier waarop de menselijke psychologie de performance van een systeem kan beïnvloeden.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Bron: &lt;a href="https://twitter.com/GGDGHORNL/status/1267351996379643906"&gt;@ggdghornl&lt;/a&gt;, Twitter&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="de-situatie"&gt;De situatie&lt;/h2&gt;
&lt;p&gt;“Voor die 30.000 tests per dag zijn we nu eindelijk klaar”, &lt;a href="https://nos.nl/collectie/13824/artikel/2335758-ggd-en-klaar-voor-massaal-testen-tijd-van-limiterende-factoren-voorbij"&gt;zei arts Ann Vossen&lt;/a&gt; op 31 mei. Dit aantal verwijst naar de &lt;a href="https://nos.nl/artikel/2335790-drukte-op-nieuw-telefoonnummer-voor-coronatests-0800-1202.html"&gt;landelijke capaciteit&lt;/a&gt; van alle GGD’s. In plaats daarvan werden volgens het AD maar 1146 mensen getest en, misschien belangrijker, een kleine 5478 afspraken gemaakt. De situatie werd erger gemaakt door de &lt;a href="https://www.ad.nl/binnenland/liefst-323-000-telefoontjes-naar-nieuw-nummer-voor-coronatest-systemen-overbelast~a16a9a50/"&gt;323.000 mensen&lt;/a&gt; die de afspraaklijn op de eerste dag hebben gebeld. Sommige medewerkers konden zelfs &lt;a href="https://www.volkskrant.nl/nieuws-achtergrond/coronatestnummer-kampt-met-kinderziektes-maar-ggd-s-kunnen-de-drukte-goed-aan~b2f332d2/"&gt;niet in de systemen inloggen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Op Twitter vertelden gebruikers enkele horrorverhalen: één beweerde &lt;a href="https://twitter.com/Ed_van_Iterson/status/1267429180347691011"&gt;6 uur lang te hebben gewacht&lt;/a&gt;, alleen om een melding te horen dat het systeem geen afspraken kon aanmaken; een andere klaagde over &lt;a href="https://twitter.com/maticus_spazz/status/1267374692941418496"&gt;verbroken verbindingen&lt;/a&gt;, zelfs toen een medewerker al had geantwoord, en nog een andere &lt;a href="https://twitter.com/leovanlinden/status/1267408142813605888"&gt;een video had gepost&lt;/a&gt; van een gesprek dat beëindigd was zonder melding. Ik heb zelf ook de &lt;a href="https://twitter.com/n_vanderhoeven/status/1271054286483533825"&gt;coronalijn gebeld&lt;/a&gt;, en nieuwe problemen gevonden.&lt;/p&gt;
&lt;p&gt;Gelukkig was de tweede dag beter: er waren 11 duizend afspraken gemaakt, mede vanwege het lagere aantal telefoontjes en mede vanwege verholpen technische problemen. In de eerste week nam het aantal geteste mensen toe naar 50.000 (gemiddeld 7142 per dag), wat toch nog veel lager zat dan verwacht. Er zijn wat lessen die we kunnen trekken uit dit verhaal.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-02.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Bron: &lt;a href="https://ggdhm.nl/thema-s/item/infectieziekten/testbeleid"&gt;GGD Hollands Midden&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="het-gevolgen-van-angst-op-performance"&gt;Het gevolgen van angst op performance&lt;/h2&gt;
&lt;p&gt;Dit virus heeft ons al een kant van de mensheid getoond die we liever niet willen zien: &lt;a href="https://www.bbc.com/news/world-australia-51731422"&gt;vechten over wc-papier&lt;/a&gt;, &lt;a href="https://snacks.robinhood.com/newsletters/rMxwcb7y7lMZoI5NY6qf7/"&gt;chaos op de beurs&lt;/a&gt;, en &lt;a href="https://www.volkskrant.nl/nieuws-achtergrond/nctv-waarschuwt-voor-extremistische-protestacties-door-5g-tegenstanders-die-vrezen-voor-coronavirus~bf1400b8/"&gt;sabotage van zendmasten&lt;/a&gt;. Hoewel we rationeel willen zijn, is het onmogelijk te zeggen dat dat altijd het geval is. In werkelijkheid hebben we allemaal de neiging gekke dingen te doen. Vooral als we angst hebben.&lt;/p&gt;
&lt;p&gt;Geen wonder dan dat de afspraaklijn zo welkom was. 323.000 telefoontjes werden er op de eerste dag verwerkt, wat bijzonder is als we bedenken dat het grootste deel van Nederland in zelfisolatie was, en dus maar een kleine kans had om het coronavirus opgelopen te hebben. In theorie lijkt dit gedrag irrationeel, maar niet onvoorspelbaar.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-03.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;In het Engels heet het &lt;strong&gt;FOMO&lt;/strong&gt;: Fear Of Missing Out. We hebben al &lt;a href="https://www.flood.io/blog/fomo-and-performance-testing-why-robinhood-went-down"&gt;duidelijk gezien&lt;/a&gt; hoe FOMO veel meer gebruikers naar een applicatie kan dwingen, met als gevolg bijwerkingen op de performance. Mensen hadden angst dat er niet genoeg coronatesten waren en wilden de eersten in de rij zijn voordat de voorraad helemaal op was.&lt;/p&gt;
&lt;p&gt;Ook was er het probleem dat &lt;a href="https://www.trouw.nl/binnenland/de-balans-na-een-week-testen-het-plan-was-mooier-dan-de-praktijk~b86399b7/"&gt;één op de vier telefoontjes niet over een afspraak ging&lt;/a&gt;, maar over algemene informatie over COVID-19. Dit werd verergerd door het feit dat de eerste dag van de coronalijn ook Tweede Pinksterdag was. Het teveel aan telefoontjes over andere zaken dan de coronatest was een bijkomende factor in de systeemoverbelasting.&lt;/p&gt;
&lt;p&gt;Wat hadden we kunnen doen om dit systeem te verbeteren?&lt;/p&gt;
&lt;h2 id="verbeteringen"&gt;Verbeteringen&lt;/h2&gt;
&lt;h3 id="beter-inschatten"&gt;Beter inschatten&lt;/h3&gt;
&lt;p&gt;Hoewel het moeilijk is om de gevolgen van FOMO precies te kennen, kunnen we het wel inschatten. &lt;a href="https://www.zwollenu.nl/500-000-tot-700-000-nederlanders-besmet-met-coronavirus/"&gt;Volgens RIVM-hoofd Jaap van Dissel&lt;/a&gt;, waren er begin mei tot 700.000 mensen in Nederland besmet met het coronavirus, een paar weken voor de coronalijn werd opgestart. Waarom was het dan een verrassing dat meer dan 300.000 mensen hadden gebeld? Ook zijn de cijfers van niet-COVID-gerelateerde klachten daar nog niet bij inbegrepen.&lt;/p&gt;
&lt;p&gt;Zelfs als we inschatten dat &lt;a href="https://www.who.int/docs/default-source/coronaviruse/situation-reports/20200306-sitrep-46-covid-19.pdf?sfvrsn=96b04adf_4"&gt;20% van die 700.000 mensen geen klachten had&lt;/a&gt;, komen we toch op een voorspelling van 140.000 telefoontjes uit, wat nog &lt;strong&gt;25 keer&lt;/strong&gt; meer is dan het werkelijke aantal gemaakte afspraken op de eerste dag.&lt;/p&gt;
&lt;p&gt;Zodra wij aantallen hebben, kunnen we testen.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-04.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Uit mijn eigen ervaring, toen ik het coronanummer belde, blijkt dat er een telefonieserver was die de oproepen heeft doorgeschakeld naar verschillende medewerkers (die thuis werkten). In deze stap konden loadtesten worden uitgevoerd om zeker te weten hoeveel gesprekken deze server aankon, en dat het snel genoeg was om geen of weinig message queueing te hebben.&lt;/p&gt;
&lt;p&gt;Medewerkers hebben dan vermoedelijk gegevens direct in een afsprakensysteem ingevoerd tijdens de oproep, waarop de gegevens waarschijnlijk naar een backend server en database gingen. Ook hier kunnen we testen via een load testing tool op protocol niveau, zoals JMeter.&lt;/p&gt;
&lt;p&gt;Met zo’n publieke startdatum zou ik spike testen hebben gevoerd met tot 700.000 gebruikers (misschien nog meer, als ik wat tijd had). In een spike test simuleren we een snelle toename van gebruikers op een kort tijdstip, bijvoorbeeld 1 tot 700.000 gebruikers in 10 minuten. Ook zouden soak testen heel nuttig zijn geweest, vooral omdat de coronalijn van 08.00 tot 20.00 was geopend, dus een stroom van 12 uur. In een soak test voeren we een constante aantal gebruikers voor een langere periode op.&lt;/p&gt;
&lt;h3 id="automatisering-gebruiken"&gt;Automatisering gebruiken&lt;/h3&gt;
&lt;p&gt;Het maken van een afspraak voor de coronatest had veel stappen. Eerst moesten mensen hun BSN en andere details aan de medewerkers geven. Dan moesten medewerkers vragen naar hun klachten. Alleen met de &lt;a href="https://www.zwollenu.nl/500-000-tot-700-000-nederlanders-besmet-met-coronavirus/"&gt;belangrijkste klachten&lt;/a&gt; die passen bij het coronavirus konden mensen verdergaan. Uiteindelijk moesten medewerkers adressen controleren en het dichtstbijzijnde testcentrum vinden om de afspraak te maken.&lt;/p&gt;
&lt;p&gt;Telefoniesystemen gebruiken soms Interactive Voice Response (IVR) technologie, een systeem van menu’s en keuzen. We hebben dit allemaal gebruikt: “Voor het Nederlands, toets 1. Voor het Engels, toets 2…” Zo’n systeem had het aantal niet afspraak-gerelateerde gesprekken kunnen verlagen met 25% (één op vier klanten belden niet voor een afspraak), en op een geautomatiseerde manier.&lt;/p&gt;
&lt;p&gt;Hier is een voorbeeld van een IVR-systeem:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-05.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Bron: &lt;a href="http://vanguardnetworks.com.sg/IVR.html"&gt;Vanguard Networks&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Op het niveau van het IVR-systeem kan het wel moeilijk zijn om load te simuleren. &lt;a href="https://www.empirix.com/products/hammer/"&gt;Empirix Hammer&lt;/a&gt; and &lt;a href="https://cyara.com/genesys-cloud/"&gt;Cyara&lt;/a&gt; zijn twee zeer gespecialiseerde tools die dit kunnen doen.&lt;/p&gt;
&lt;p&gt;Nog een optie voor de implementatie van een afsprakensysteem is een web app, met verplichte velden voor BSN, adressen, enz, en met een verbinding met DigiD, een identiteitsverificatiesysteem. &lt;a href="https://mijn.overheid.nl/"&gt;Mijn Overheid&lt;/a&gt; bestaat al, en ook met DigiD integratie. Er had een soort van beveiligde formulier kunnen worden gebruikt om klantengegevens snel op te nemen.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-06.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Bron: &lt;a href="https://mijn.overheid.nl/"&gt;Mijn Overheid&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="end-to-end-testing-uitvoeren"&gt;End-to-end testing uitvoeren&lt;/h3&gt;
&lt;p&gt;Vaak spreken we over de performance van één applicatieserver, maar in praktijk zijn er meerdere componenten. Als best practice is het goed om de performance van elk onderdeel van het hele systeem te testen, en dan ook de hele keten.&lt;/p&gt;
&lt;p&gt;Voor een afsprakenlijn zou het redelijk zijn geweest om het hele proces te testen: het oproepdoorschakelingssysteem, het afspraaksysteem, de database met gegevens en datums, de servers van elke GGD in het land, en het sturen van email en SMS-berichten met afspraak datums. Het is belangrijk om te weten of alle componenten snel samen kunnen werken.&lt;/p&gt;
&lt;p&gt;Ook is er de performance van niet-computer onderdelen. De menselijke component kan ook een bottleneck zijn. Wisten de medewerkers hoe snel ze moesten vragen of een klant een afspraak wilde maken? Kunnen de testen worden uitgevoerd en de uitslagen worden gegeven, zelfs met een snelstromend afspraaksysteem? Het teveel aan afspraken toegewezen aan GGD’s kan ook files veroorzaken (zoals in &lt;a href="https://www.ed.nl/eindhoven/in-de-file-voor-een-stukje-zekerheid-meteen-drukte-bij-teststraat-voor-corona-in-eindhoven~a27eec83/"&gt;Eindhoven&lt;/a&gt; en &lt;a href="https://www.pzc.nl/zeeuws-nieuws/drukte-bij-teststraat-in-goes-na-openstellen-nummer-voor-coronatest~aa8a6cb2/"&gt;Goes&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id="conclusie"&gt;Conclusie&lt;/h2&gt;
&lt;p&gt;In dit geval had slechte performance een invloed niet op de winst maar op de gezondheid. Zieke personen die klachten hadden, konden niet allemaal een afspraak maken om getest te worden. Hoeveel andere mensen hadden ze inmiddels besmet? Slechte performance heeft medewerkers ook gedwongen om soms uitslagen te geven “&lt;a href="https://wnl.tv/2020/06/08/chaos-bij-de-coronalijn-personeel-slaat-alarm/"&gt;in verdekte termen&lt;/a&gt;”, wat tot een privacyschending kan leiden.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200704-07.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;De menselijke component kan een systeem lastiger maken om te testen. Aldus &lt;a href="https://www.trouw.nl/binnenland/de-balans-na-een-week-testen-het-plan-was-mooier-dan-de-praktijk~b86399b7/"&gt;Andrea Evers (LUMC)&lt;/a&gt;, hoogleraar gezondheidspsychologie:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Door de uitbraak van corona zijn mensen in een langdurige stresssituatie beland. Onzekerheid, onvoorspelbaarheid en oncontroleerbaarheid maken dat ze de behoefte hebben om zaken juist wel te controleren of voorspelbaar te maken.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Als performancetesters kunnen we statistieken en een systematische aanpak combineren met kennis van de menselijke factoren om te plannen voor het onvoorspelbare.&lt;/p&gt;</description></item><item><title>Ask a Flooder 17: How do I use unique test data on Element?</title><link>https://nicolevanderhoeven.com/blog/20200618-aaf17/</link><pubDate>Thu, 18 Jun 2020 21:58:49 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200618-aaf17/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-17-how-do-i-use-unique-test-data-on-element"&gt;
&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://www.flood.io/blog/ask-a-flooder-17-how-do-i-use-unique-test-data-on-element"&gt;Tricentis Flood&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this video, I talk about how to ensure that Flood Element uses unique test data, using the example of a customer wanting to script unique logins for his application.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/qVpX7MX3jxE?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Or listen to the audio version instead:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/7k5TXtoHnswJwBbKHXgJR7" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="transcript"&gt;Transcript:&lt;/h2&gt;
&lt;p&gt;Hi everyone, Nicole van der Hoeven here again back with another Ask a Flooder, and this week&amp;rsquo;s question is a follow-up question from last week. That user had previously asked about test data in Flood Element. This week he&amp;rsquo;s asking: &amp;ldquo;How do I ensure that Flood Element uses unique test data?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So this user is actually trying to script a login for an application. He has a list of usernames and corresponding passwords that he wants to use. He&amp;rsquo;s already set both of these up in a CSV file, and he&amp;rsquo;s also set up the Element script in the same way that I outline in my previous video. However, this user is seeing errors in the test due to the application not allowing a given user account to be used simultaneously in two or more devices. The problem is that the way that he&amp;rsquo;s set it up is that he has Element running on multiple nodes, and each node has the chance to pick the same user account.&lt;/p&gt;
&lt;p&gt;So how do you make sure that it&amp;rsquo;s a unique 1:1 ratio for an instance of Element and a user account in the CSV file? If you haven&amp;rsquo;t already watched the previous week&amp;rsquo;s video, you should pause this, watch that, and then come back here, so it makes a little more sense because the script that I&amp;rsquo;m using is building on what we already did in the previous week.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s talk about the differences between this script and the previous week&amp;rsquo;s.&lt;/p&gt;
&lt;p&gt;First, I&amp;rsquo;m declaring a global variable here called &lt;code&gt;globalBrowserID&lt;/code&gt;. The idea is to create a unique and globally identifiable string that I can attach to every account in the CSV file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;globalBrowserID&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ENV&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FLOOD_GRID_INDEX&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;_&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ENV&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FLOOD_GRID_NODE_SEQUENCE_ID&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;_&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ENV&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;BROWSER_ID&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;toString&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The global browser ID consists of three parts:- The first is a grid index, which is a number assigned to every grid, and it&amp;rsquo;s also globally unique. So if there are three grids being used in the test, then the grid indexes would be 0, 1, and 2.- The second one is a grid node sequence ID, and this is unique within the grid. The node sequence ID is a number that is assigned to every node in a single grid.- The last one is a browser ID. The browser ID is a number assigned to an instance of Element running on a single node.&lt;/p&gt;
&lt;p&gt;These are all zero-based, so the very first instance of Element running on the very first node and on the very first grid in the entire test would be &lt;code&gt;0_0_0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re going to have to modify our CSV file to make use of this browser ID. So whereas the user previously had username and password, I&amp;rsquo;ve added an ID column. From then, these will have to be sequential. So &lt;code&gt;0_0_0&lt;/code&gt;, &lt;code&gt;0_0_1...&lt;/code&gt; And &lt;code&gt;0_0_1&lt;/code&gt; would be the second instance of Element running still on the first node, on the first grid. And you can make as many of these as you need.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;id,username,password
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0_0_0,user1,password1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0_0_1,user2,password2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0_0_2,user3,password3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1_0_0,user4,password4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1_0_1,user5,password5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve also modified the interface &lt;code&gt;UserData&lt;/code&gt; to describe the fields in the CSV file. So there&amp;rsquo;s &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, and &lt;code&gt;password&lt;/code&gt;, and I&amp;rsquo;ve defined them all as strings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;TestData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;fromCSV&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;UserData&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;users_multiplegrids.csv&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;filter&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;line&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;index&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;browserID&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;line&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;globalBrowserID&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;circular&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This line is telling Element to take this data file with this filename and filter it based on the line, the index, and the browserID such that &lt;code&gt;id&lt;/code&gt; is equal to the browser ID. So what happens here is that Element will take the global browser ID, which let&amp;rsquo;s say is &lt;code&gt;0_0_0&lt;/code&gt; for the very first one. It&amp;rsquo;ll then take that &lt;code&gt;0_0_0&lt;/code&gt; and look at the CSV file for anything in the ID field that has the value &lt;code&gt;0_0_0&lt;/code&gt;. And then it will continue to use the rest of that line.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also made the CSV file circular just because that&amp;rsquo;s always a good practice to get into.&lt;/p&gt;
&lt;p&gt;And as always, I think it&amp;rsquo;s a great idea to debug this, especially when you&amp;rsquo;re dealing with multiple grids and multiple nodes on that grid.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Print to console&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Browser&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserData&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;global browser ID: &amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;globalBrowserID&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;| user: &amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39; | password: &amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;)    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve also added the username and password here, just in case we need to verify the state that that user is in.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s run the test. Since we&amp;rsquo;re just running this locally, we&amp;rsquo;ll see that the first value indeed is &lt;code&gt;0_0_0&lt;/code&gt;, and the user we&amp;rsquo;re selecting is &lt;code&gt;user1&lt;/code&gt; and &lt;code&gt;password1&lt;/code&gt;, which lines up with the username and password values corresponding to &lt;code&gt;0_0_0&lt;/code&gt; in the CSV file.&lt;/p&gt;
&lt;p&gt;I hope that helped, and until next time, happy flooding!&lt;/p&gt;
&lt;p&gt;Download the &lt;a href="https://github.com/flood-io/load-testing-playground/blob/master/element/test-data/example.ts"&gt;full script&lt;/a&gt; and the &lt;a href="https://github.com/flood-io/load-testing-playground/blob/master/element/test-data/users.csv"&gt;data file&lt;/a&gt; to follow along.&lt;/p&gt;</description></item><item><title>Ask a Flooder 16: How do I use test data with Element?</title><link>https://nicolevanderhoeven.com/blog/20200609-aaf16/</link><pubDate>Tue, 09 Jun 2020 22:18:41 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200609-aaf16/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-16-how-do-i-use-test-data-with-element-video"&gt;
&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://www.flood.io/blog/ask-a-flooder-16-how-do-i-use-test-data-with-element-video"&gt;Tricentis Flood&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this video, I discuss how to make your load testing scripts more realistic by including test data. The load testing tool I use is &lt;a href="https://element.flood.io/"&gt;Flood Element&lt;/a&gt;, but you can apply these principles to any tool. You&amp;rsquo;ll find a copy of the Element script I used below.&lt;/p&gt;
&lt;p&gt;‍&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/EGhVm-R12qI?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;And, if you prefer, here&amp;rsquo;s the audio version:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/4OW5slSCmAuBc9NPa1NKtk" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="transcript"&gt;Transcript&lt;/h2&gt;
&lt;p&gt;Hi everyone, Nicole van der Hoeven here back with another Ask a Flooder, and this time the question is: How do I use test data with Flood Element?&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s talk about why you might want to use test data. In load testing, it&amp;rsquo;s important to make your scripts behave as similarly to real users as possible so that the load that you generate is production-like. The problem is that if you send the same data over and over again to the server, that server is probably going to cache that response. The response being cached means that the server will return the response way faster than it normally would if it had been a unique request. So we can use test data, instead of static data hardcoded in your scripts, to make sure that this doesn&amp;rsquo;t happen.&lt;/p&gt;
&lt;p&gt;Using test data can also help us test different responses. For example, let&amp;rsquo;s say we&amp;rsquo;re testing a loan application system, where a user inputs the application ID, the server looks it up, and returns the status of that loan application. Now, loan applications might be in different stages of the process. For instance, some might be saved but not submitted, or maybe they&amp;rsquo;ve been submitted but failed the identity verification check.&lt;/p&gt;
&lt;p&gt;If we used the same application ID every time, then the server would always return the same status and the same details. Instead, let&amp;rsquo;s create a CSV file that we can use as test data. It might look something like this.‍&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;applicationId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;A100001
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;B100002
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C100003
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;D100004
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can have &lt;code&gt;applicationId&lt;/code&gt; as the single header here, and we can populate this with a list of different application IDs in different stages of the process. That way, when we run the script, the server will have to respond authentically to every request without it being cached.&lt;/p&gt;
&lt;p&gt;Now to create our script. This is an empty Element script. To start with, make sure you&amp;rsquo;re importing &lt;code&gt;TestData&lt;/code&gt; here in the first line of your script.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Browser&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;TestData&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;@flood/element&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first thing we&amp;rsquo;re going to need to do is to create an interface for the CSV file. Let&amp;rsquo;s call it &lt;code&gt;AppData&lt;/code&gt;. Within this interface, we&amp;rsquo;ll need to define all the fields that we intend to use in our data file and what type of values we can expect to be in those fields. Looking at our CSV here, we&amp;rsquo;ve just got the application ID, so I&amp;rsquo;m just going to copy that, and it&amp;rsquo;s a mixture of characters and numbers, so I&amp;rsquo;m going to put that in as a string.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;applicationId&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;string&lt;/span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need to tell Element how to find this test data. We&amp;rsquo;ll say that it&amp;rsquo;s from the CSV, using the interface we&amp;rsquo;ve just created. Now we&amp;rsquo;ll tell Element the filename. In this situation, the CSV file that I&amp;rsquo;m using is actually in the same directory, so I don&amp;rsquo;t need to specify a path here, but if you&amp;rsquo;re putting it somewhere else, then you&amp;rsquo;ll need to specify the folder and the path to it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;TestData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;fromCSV&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;applicationids.csv&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s use the data in a step. In real life, you might want to use an application ID to type it into a text field, but for demonstration purposes, I&amp;rsquo;m just going to show you how to output it to the console log. Printing to the console is also a good way to debug your tests, especially if you&amp;rsquo;re dealing with test data.&lt;/p&gt;
&lt;p&gt;We need to let Element know that we want to use AppData. So now, let&amp;rsquo;s output this to the console. Since we&amp;rsquo;re just debugging, let&amp;rsquo;s output the application ID. To actually get the value from the data file, we&amp;rsquo;ll use this syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Print to console&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Browser&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;application ID: &amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;applicationId&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;‍By default, Element reads the data files sequentially. The problem with that is we only have a handful of application IDs. So if we wanted to run this one script 100 times, we&amp;rsquo;re going to run out of data. What we can do here is add &lt;code&gt;circular&lt;/code&gt;. That way, when the script reaches the very last value of the file, the next value it selects will be the first one, and it never runs out of test data.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;TestData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;fromCSV&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;applicationids.csv&amp;#39;&lt;/span&gt;)    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;circular&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another thing you might be wondering is how to make it so that Element chooses a random value every time. To do that, we&amp;rsquo;ll also have to add &lt;code&gt;shuffle&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;TestData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;fromCSV&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;applicationids.csv&amp;#39;&lt;/span&gt;)    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;circular&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;‍Now let&amp;rsquo;s try to run the test.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;element run test-data.ts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, it&amp;rsquo;s been finished. The application ID that was selected was indeed a random one because the first one would have started with A. So, it actually chose the last one randomly.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it for test data with Element. Now, this user actually had a follow-up question, which was about unique test data. I&amp;rsquo;m going to answer that in the next video. Until next time, happy Flooding!&lt;/p&gt;
&lt;h2 id="sample-script"&gt;Sample script&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Browser&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;TestData&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;@flood/element&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;assert&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;assert&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loopCount&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;description&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Test Data&amp;#39;&lt;/span&gt;,    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;screenshotOnFailure&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;disableCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clearCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clearCookies&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/*** Test Data * Version: 1.0 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; () =&amp;gt; {    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt; {        
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;applicationId&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;string&lt;/span&gt;    }    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;TestData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;fromCSV&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;applicationids.csv&amp;#39;&lt;/span&gt;)    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;circular&lt;/span&gt;()    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;shuffle&lt;/span&gt;()    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Print to console&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Browser&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AppData&lt;/span&gt;) =&amp;gt; {        
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;application ID: &amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;applicationId&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;)    
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Load testing on the Google Cloud Platform</title><link>https://nicolevanderhoeven.com/blog/20200528-load-testing-on-google-cloud-platform/</link><pubDate>Thu, 28 May 2020 22:25:06 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200528-load-testing-on-google-cloud-platform/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/load-testing-on-the-google-cloud-platform"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/load-testing-on-the-google-cloud-platform"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re excited to welcome another cloud provider to the Flood lineup, giving you even more options for running your load tests. You can now use Flood to run load tests on the &lt;a href="https://cloud.google.com/"&gt;Google Cloud Platform (GCP)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many of you have requested an integration with Google Cloud, citing many of its strengths:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Full &lt;a href="https://cloud.google.com/blog/products/open-source/bringing-the-best-of-open-source-to-google-cloud-customers"&gt;integrations with open-source tools&lt;/a&gt; like Influx and Redis&lt;/li&gt;
&lt;li&gt;World-class &lt;a href="https://cloud.google.com/security"&gt;security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;High-performance &lt;a href="https://cloud.google.com/infrastructure"&gt;global network&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Seamless access to other apps in the Google ecosystem, such as Search and Maps&lt;/p&gt;
&lt;p&gt;We think that adding support for GCP instances opens up a lot of opportunities for flooders wanting to take advantage of these benefits.&lt;/p&gt;
&lt;h2 id="using-flood-with-google-cloud"&gt;Using Flood with Google Cloud&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll notice a new Google Cloud section in &lt;a href="https://app.flood.io/account/integrations"&gt;the Integrations page&lt;/a&gt; while you&amp;rsquo;re logged into Flood:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200528-01.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need to add your GCP service account key to allow Flood to access your account on your behalf. Once you&amp;rsquo;ve done this, you&amp;rsquo;ll be able to start grids and floods right from the Flood interface you&amp;rsquo;re already using.&lt;/p&gt;
&lt;p&gt;For more details on how to set up Flood to use your GCP account, check out &lt;a href="https://guides.flood.io/infrastructure/host-your-own-infrastructure/using-your-own-google-cloud-account"&gt;our documentation&lt;/a&gt; or follow along with the video below:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/oYTo0jwRfMo?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Anyone with a Flood subscription, including a free one, can use Google Cloud instances at no extra cost from Flood; however, GCP usage may incur separate costs.&lt;/p&gt;
&lt;p&gt;GCP is the third cloud provider that we support at Flood, after &lt;a href="https://guides.flood.io/infrastructure/host-your-own-infrastructure/using-your-own-aws-account"&gt;Amazon Web Services&lt;/a&gt; and &lt;a href="https://guides.flood.io/infrastructure/host-your-own-infrastructure/using-your-own-azure-account"&gt;Microsoft Azure&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Ask a Flooder 15: How do I install Element?</title><link>https://nicolevanderhoeven.com/blog/20200512-aaf15/</link><pubDate>Tue, 12 May 2020 22:45:18 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200512-aaf15/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-15-how-do-i-install-element-video"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-15-how-do-i-install-element-video"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this Flood Element tutorial, I demonstrate the steps required to install Flood Element, including dependencies it requires. Flood Element is an open-source browser-based load testing tool created by Tricentis Flood.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://bit.ly/2YM1mnV"&gt;Learn more about Flood Element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://nodejs.org/en/download/current/"&gt;Installation instructions for NodeJS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/en/docs/install/"&gt;Installation instructions for Yarn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://bit.ly/2yuYrWl"&gt;Installation instructions for Element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=oWtnStKrh98"&gt;Video: Getting Started with Element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://bit.ly/2YJIQg5"&gt;Guides: Using Element with Flood‍&lt;/a&gt;&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/N7VLqqueqJE?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Audio version here:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/6EZ97ImNGUiDAGcEyGGNYM" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="transcript"&gt;Transcript&lt;/h2&gt;
&lt;p&gt;Hi everyone, Nicole van der Hoeven here again, back with another Ask a Flooder, and today&amp;rsquo;s question is: &amp;ldquo;How do I install Element?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Now, I already made a previous video on how to get started with Element, but I didn&amp;rsquo;t actually go into the details of how to install it, so that&amp;rsquo;s what I&amp;rsquo;m going to do in this video today. There are actually a few parts to installing Element, and all of these instructions are going to be on &lt;a href="http://element.flood.io/"&gt;element.flood.io&lt;/a&gt;. If you go onto the site and click on Install, you&amp;rsquo;ll see all of the instructions depending on which operating system you&amp;rsquo;re on.&lt;/p&gt;
&lt;p&gt;First, you need to install NodeJS. This is sort of like how, if you&amp;rsquo;re familiar with JMeter, you have to install Java before installing JMeter. NodeJS is what Element runs on. Secondly, you&amp;rsquo;ll need a package manager. You can use NPM or Yarn, but in this video, I&amp;rsquo;m going to take you through how to do it with Yarn. And thirdly, there&amp;rsquo;s installing Element itself.&lt;/p&gt;
&lt;p&gt;First, download NodeJS. I&amp;rsquo;m going to leave links in the description to all of the sites that I visit so that you can go through all of the instructions later. On the Node site, download whichever installer suits your operating system. Go through the installation steps, and afterwards, check to make sure that NodeJS is properly installed on your machine. You can do that by going into the terminal and typing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;node -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Just make sure that the version that&amp;rsquo;s shown here is the same version that you&amp;rsquo;ve downloaded.&lt;/p&gt;
&lt;p&gt;Secondly, install the latest version of Yarn. On the Yarn site, you&amp;rsquo;re going to have to choose your operating system and the version. We recommend using the latest stable version. And then go through the instructions for your operating system. After you&amp;rsquo;ve done that, check your Yarn installation by typing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yarn --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the last step is installing Element. On the Element site, you can find the exact code that you need to type into your terminal. It&amp;rsquo;s:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yarn global add @flood/element-cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So you can just copy that into your terminal. As of the time of this recording, the latest version of Element is 1.2.3. Looks like that&amp;rsquo;s installed. To check the version of Element, type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;element --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So that&amp;rsquo;s how to install Element. We&amp;rsquo;ve gotten a lot of questions recently about Element in particular, so I&amp;rsquo;m going to answering them in future videos. Hope this helped, and until next time, happy flooding!&lt;/p&gt;</description></item><item><title>Innovation in Isolation</title><link>https://nicolevanderhoeven.com/blog/20200505-innovation-in-isolation/</link><pubDate>Tue, 05 May 2020 22:59:57 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200505-innovation-in-isolation/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/innovation-in-isolation"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/innovation-in-isolation"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve always been a fully distributed team at Flood, but the current world crisis has still changed the way we work. Here are some of the things we&amp;rsquo;re doing to keep self-isolation from being &lt;em&gt;too&lt;/em&gt; isolating— and maybe it&amp;rsquo;ll help your team too!&lt;/p&gt;
&lt;p&gt;Within the Flood team, flooders-in-training (aka children) have made more surprise appearances in team meetings. Work has been punctuated with grocery runs to buy essentials before they run out. Some of us who were out of our home countries when all this happened have had to arrange emergency travel before international borders shut. It would be foolish to deny the impact on team morale as the entire world goes through this collective trauma, eyes glued to numbers that increase by the minute.&lt;/p&gt;
&lt;p&gt;Not exactly the most productive work environment.&lt;/p&gt;
&lt;p&gt;Yet there&amp;rsquo;s still work to be done (something we&amp;rsquo;re grateful for), and there are still many features we&amp;rsquo;d love to bring to Flood. So how do we keep everyone motivated to release and maintain software?&lt;/p&gt;
&lt;h2 id="encouraging-banter"&gt;Encouraging banter&lt;/h2&gt;
&lt;p&gt;Work shouldn&amp;rsquo;t be all about work. Understanding everyone&amp;rsquo;s personal circumstances is vital to a team&amp;rsquo;s ability to work together professionally. We use Slack as our primary communication tool throughout the day, and we already had quite a few non-work channels on there (#anything, #hobbies, #gaming, #drones, and #travel to name a few). Now we have one for COVID-related chatter as well.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;In this channel, we share news from our parts of the world, post photos of things that are sold out in our local supermarkets, talk about new hobbies we&amp;rsquo;ve taken up to stay sane, and discuss the struggles of co-working with spouses and children who are now also working or studying from home.&lt;/p&gt;
&lt;p&gt;Having a dedicated channel gives people license to open up about what&amp;rsquo;s going on in their personal lives, and it helps the team understand where everyone&amp;rsquo;s headspace is at. We also use #eng-covid-19 for memes. A lot of memes. It&amp;rsquo;s easier to work through a tough situation if you can laugh about it together.&lt;/p&gt;
&lt;h2 id="over-communicating"&gt;Over-communicating&lt;/h2&gt;
&lt;p&gt;Our co-founder and Product Manager, Ivan Vanderbyl, recently &lt;a href="https://enterprisersproject.com/article/2020/4/9-collaboration-tool-tips-remote-teams"&gt;wrote a great article&lt;/a&gt; with some tips for keeping communication lines open. This is particularly important during this crisis, where schedules and priorities can change quickly.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Emojis - We use emojis as a non-verbal cue to convey tone, approval, agreement, and disagreement. We add emojis to messages we send and also use them as reactions on Slack to messages from other people. Even when you don&amp;rsquo;t have an answer or when a response is not required, emojis signal acknowledgment so that nobody feels like they&amp;rsquo;re shouting into the void. Over time, we&amp;rsquo;ve developed emojis that are specific to Flood culture, such as the avocado:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-02.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Source: Our &lt;a href="https://twitter.com/flood_io/status/1230166412066512896"&gt;Twitter account&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;‍We even have a fruit-based vocabulary of emojis that we can use in our Slack status messages to convey our general mood. Being in Carrot Mode, for instance, means that you&amp;rsquo;re trying to focus and would appreciate not being direct messaged or @ mentioned.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-03.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;‍When working remotely, and especially when in self-isolation, everyone has their own cadence for working. It&amp;rsquo;s on each person to communicate that schedule so that others can respect it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Text chat - We&amp;rsquo;re used to working asynchronously due to time zones, so text-based discussions are necessary. We try to over-communicate and &amp;ldquo;think out loud&amp;rdquo; before making decisions just in case we have to suddenly stop working due to illness or other pandemic-related reasons. Even something as simple as leaving a link to a document that we&amp;rsquo;re working on means that someone else will be able to find it if we&amp;rsquo;re unavailable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Voice chat - We tend to use voice chat for quick discussions or updates about work-related things. It&amp;rsquo;s also great for those of us in Europe who are suffering from slower internet connection speeds at certain times of the day due to everyone being home and streaming Netflix.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Video calls - Video calls are for team retros, 1:1 calls with managers, and personal or sensitive conversations where body language is crucial. It&amp;rsquo;s also great for recreating that &amp;ldquo;hanging out at a pub&amp;rdquo; vibe. Speaking of which&amp;hellip; welcome to the Flood pub!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-04.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Flood Pub is open on Friday afternoons. It&amp;rsquo;s where we all hang out together with a beverage of our choice and talk about things that are entirely not work-related. We hop on a Zoom call together, Tim (our General Manager) fires up the &amp;ldquo;jukebox&amp;rdquo; (Spotify playlist), and we end the week by getting some facetime with each other—just as we would do (and have done) if we were together in person.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-05.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;‍Meeting in this low-pressure context lets us talk about things we wouldn&amp;rsquo;t mention otherwise. It&amp;rsquo;s also a fun way to check in with each other after another week at work (and in quarantine).&lt;/p&gt;
&lt;h2 id="doing-stuff"&gt;Doing stuff&lt;/h2&gt;
&lt;p&gt;While communicating openly is productive, getting stuff done as a team boosts spirits.  Here&amp;rsquo;s what we&amp;rsquo;ve done recently to kick our output into high gear.&lt;/p&gt;
&lt;h3 id="boinc"&gt;BOINC&lt;/h3&gt;
&lt;p&gt;Like most of you, we&amp;rsquo;ve been cooped up in our houses, watching events unfold and feeling helpless as things happen that are beyond our control. BOINC gives us a chance to do something to contribute that IS in our control.&lt;/p&gt;
&lt;p&gt;BOINC is a platform by the University of California Berkeley that lets you volunteer your spare computer (or mobile device!) processing power to science. From the official site, Boinc &amp;ldquo;downloads scientific jobs to your computers and runs them invisibly in the background.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;In our case, we wanted to help sequence proteins like the novel coronavirus using Rosetta@Home. BOINC is a perfect fit for Flood because we are in the unique position of starting up and managing thousands of virtual machines 24/7. Our Ops team has been hard at work automating the donation of our computing power to the Rosetta@Home project when these machines aren&amp;rsquo;t being used.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-06.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re proud to say our contribution is INcreasing over time as our engineers find new ways to streamline the process. We&amp;rsquo;re already in the &lt;a href="https://www.boincstats.com/stats/-1/user/detail/87605321733/overview"&gt;top 5% of BOINC users in the world&lt;/a&gt; by contribution.&lt;/p&gt;
&lt;h3 id="gig-great-ideas-gathering"&gt;GIG: Great Ideas Gathering&lt;/h3&gt;
&lt;p&gt;Every year, the mothership (Tricentis) runs a three-day hackathon called GIG. Two weeks ago was the first-ever GIG where most participants, not just the Flood team, were working remotely.&lt;/p&gt;
&lt;p&gt;The theory behind hackathons is that the daily grind can be exhausting after a while, and it can sometimes be beneficial to introduce a short burst of work to revitalize the team. Hackathons provide a framework for small groups to pitch, execute, and report on a single idea without committing too much time to it. Everyone gets a chance to step away from regular duties and work furiously together on something they might not have otherwise gotten to work on.&lt;/p&gt;
&lt;p&gt;A hackathon where the team members are separated by distance and timezone is certainly another beast, but here are some lessons we learned about remote hackathons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Come up with a good idea&lt;/strong&gt;: This is easier said than done, but a good hackathon idea is a creative solution that delivers a lot of value. Here are the ideas we came up with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Floody - Use AI to improve onboarding by walking new users through the steps for running a flood (live on Flood now!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Flood Bot - Add smarter checks to detect patterns in load tests and help flooders understand their results (prototype)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Flood Gate - Use AWS Fargate to reduce costs in provisioning, scaling, and managing virtual servers by about 30% (proof of concept)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When thinking up ideas for GIG, we didn&amp;rsquo;t commit to releasing them publicly. Even a project idea that doesn&amp;rsquo;t end up making it into the finished product is an occasional worthwhile detour for team productivity and morale.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limit scope&lt;/strong&gt;: GIG is a sprint, not a marathon. While my team (comprised of Jason Rizio and me) was planning out what we wanted to do, we quickly realized that there was a lot more that we wanted to do, but it just wasn&amp;rsquo;t feasible within the strict timeframe. In the end, we decided to go for an incremental piece of work that we could push to production before the deadline instead of completing just one step towards something abstract and unreleasable.The other Flood teams also needed to whittle down the scope to a smaller, completable chunk: a working prototype.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clarify assignments&lt;/strong&gt;: With members on different timezones, it was essential to have an initial meeting to plan out everything that needed to be done and by when. In this situation particularly, we found it useful to split apart the project into independent chunks, to minimize the need to collaborate in real-time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Touch base regularly&lt;/strong&gt;: When working independently, team members can quickly diverge off the main path. To avoid that, we met up at least daily by hopping on an audio call on Zoom to spend a few minutes updating each other on the work we&amp;rsquo;d done. Doing this gives others a chance to help out when one member encounters roadblocks.For anything that didn&amp;rsquo;t need to be real-time, we worked asynchronously on chat. Here&amp;rsquo;s a screenshot of the number of messages and files exchanged to our dedicated GIG Slack channel during the event:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-07.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;‍Yup, 1,535 messages and 60 files in 3 days. That&amp;rsquo;s collaboration!&lt;/p&gt;
&lt;p&gt;We generally prefer public rather than private channels, and we certainly stuck to that throughout GIG. When you&amp;rsquo;re working with more than one person, it&amp;rsquo;s counterproductive to have private conversations that can&amp;rsquo;t be referenced by other people.&lt;/p&gt;
&lt;p&gt;We also learned that it&amp;rsquo;s useful to keep tabs on what the other teams are doing. After the event started, my team realized that another team was changing a page on the Flood app that we intended to use. Luckily, both teams were able to work around it.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also only fair to let other teams chime in on important decisions within the project that might affect future work— like the name for your mascot.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200505-08.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Leave time for the video&lt;/strong&gt;: It&amp;rsquo;s not just about doing the work; it&amp;rsquo;s about communicating what you&amp;rsquo;ve done in a way that others can appreciate. Here&amp;rsquo;s a video that my team (composed of Jason Rizio and me) put together for Floody and our improvements to the onboarding process:&lt;/li&gt;
&lt;/ul&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/OeIPWovOua4?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Celebrate&lt;/strong&gt;: After the work is done, don&amp;rsquo;t forget to pat each other on the back for the hard work! For us, this meant &lt;a href="https://twitter.com/c_a_dunlop/status/1246111919762591746"&gt;a round of tacos&lt;/a&gt; for everyone and a trip to the Flood pub. (Why do we have so many food-based rewards?)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="its-not-easy"&gt;It&amp;rsquo;s not easy&lt;/h2&gt;
&lt;p&gt;Keeping a team unified and working efficiently towards a common goal is always tricky, and working remotely, especially during a world crisis, doesn&amp;rsquo;t change that. Distributed team members need to put in more effort to consciously over-communicate, make room for each other&amp;rsquo;s unique circumstances, and do little projects that foster creativity when we&amp;rsquo;re all feeling a little lackluster. Incorporating these tips can create a culture of healthy productivity within a remote team.&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;</description></item><item><title>On-premise load testing advantages and disadvantages</title><link>https://nicolevanderhoeven.com/blog/20200430-on-premise-load-testing/</link><pubDate>Thu, 30 Apr 2020 23:06:59 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200430-on-premise-load-testing/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/on-premise-load-testing-advantages-and-disadvantages"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/on-premise-load-testing-advantages-and-disadvantages"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Where you run your load test can affect the results that you get as well as your project&amp;rsquo;s bottom line. In general, there are two infrastructure options to consider for load testing your apps: on-premise and cloud. Which type of environment is best for load testing both web or mobile apps?&lt;/p&gt;
&lt;h2 id="differences-between-on-premise-and-cloud-load-testing"&gt;Differences between on-premise and cloud load testing&lt;/h2&gt;
&lt;h3 id="what-is-on-premise-load-testing"&gt;What is on-premise load testing?&lt;/h3&gt;
&lt;p&gt;On-premise load testing is running your performance or load testing scripts on load generators you physically control. Typically, these load generators are machines that are either in your office or data center. In on-premise load testing, you are responsible for all aspects of the maintenance of the load generators, including hardware upgrades and software installations such as testing tools, but that also comes with increased control and security.&lt;/p&gt;
&lt;h3 id="what-is-cloud-load-testing"&gt;What is cloud load testing?&lt;/h3&gt;
&lt;p&gt;In cloud load testing, you run your load tests on a service provider&amp;rsquo;s machines, effectively outsourcing the provisioning and maintenance of your load generators. Running &amp;ldquo;on the cloud&amp;rdquo; means you&amp;rsquo;re paying someone else to be responsible for these machines. In cloud load testing, you don&amp;rsquo;t &amp;ldquo;own&amp;rdquo; the machines your test is running on; it&amp;rsquo;s more like renting some time on someone&amp;rsquo;s data center whenever you need it. Cloud-based infrastructure isn&amp;rsquo;t necessarily less secure than on-premise infrastructure, but there are certainly more security considerations to keep in mind.&lt;/p&gt;
&lt;h3 id="pros-and-cons-of-on-premise-load-testing"&gt;Pros and cons of on-premise load testing&lt;/h3&gt;
&lt;p&gt;On-premise load testing is no better or worse than cloud load testing; it just depends on what you need. There&amp;rsquo;s a meme that the cloud is just a fancy term for someone else&amp;rsquo;s computer— but &lt;a href="https://www.zdnet.com/article/stop-saying-the-cloud-is-just-someone-elses-computer-because-its-not/"&gt;that&amp;rsquo;s an oversimplification&lt;/a&gt; that glosses over nuances that might make a difference to your bottom line.&lt;/p&gt;
&lt;p&gt;Here are some questions to ask yourself when deciding between load testing on the cloud or on-premise.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200430-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Test infrastructure can make or break your load tests.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="cloud-vs-on-premise-load-testing-how-do-you-decide"&gt;Cloud vs. on-premise load testing: how do you decide?&lt;/h3&gt;
&lt;h4 id="why-are-you-running-a-load-test"&gt;Why are you running a load test?&lt;/h4&gt;
&lt;p&gt;If your intention for running load tests is to test your web application&amp;rsquo;s load balancing, the source of the web traffic, and the location of your environment, may not be as important. If you intend to do stress testing, you may want the scalability of a cloud setup.&lt;/p&gt;
&lt;h4 id="who-is-the-intended-audience-of-your-application"&gt;Who is the intended audience of your application?‍&lt;/h4&gt;
&lt;p&gt;Load testing is most accurate when it is as realistic to the production workload and user base as possible. Running a load test on-premises means that the traffic comes from one location, which could be ideal if your application is an internal one.&lt;/p&gt;
&lt;h5 id="do-you-have-the-resources-in-your-teamcompany-to-maintain-load-generators"&gt;Do you have the resources in your team/company to maintain load generators?&lt;/h5&gt;
&lt;p&gt;All machines need to be provisioned, set up, and maintained to function correctly. Depending on how many machines you need, you may even need a team of trained professionals to monitor your environment.&lt;/p&gt;
&lt;h4 id="what-is-your-budget"&gt;What is your budget?&lt;/h4&gt;
&lt;p&gt;Both cloud and on-premise load testing have costs. Cloud load testing requires a smaller outlay of cash initially but may cost more to maintain as you continue to pay for the computing time. On-premise load testing requires more money upfront but may cost less to maintain in the long run.&lt;/p&gt;
&lt;h4 id="how-many-virtual-users-do-you-want-to-run"&gt;How many virtual users do you want to run?&lt;/h4&gt;
&lt;p&gt;The number of virtual users you intend to test your application against correlates to the number of load generators you require. In general, the more machines you need, the more expensive it becomes (for both cloud and on-premise load testing), but if you only need a handful of users, perhaps you can find a machine or two on-premise that could do the trick.&lt;/p&gt;
&lt;h3 id="advantages-of-on-premise-load-testing"&gt;Advantages of on-premise load testing&lt;/h3&gt;
&lt;p&gt;‍
&lt;img src="https://nicolevanderhoeven.com/assets/20200430-02.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h4 id="increased-control"&gt;Increased control&lt;/h4&gt;
&lt;p&gt;Since you own the machines you&amp;rsquo;re testing on, you can set your environment up with your exact specifications in mind. You&amp;rsquo;ll be able to make hardware upgrades or update versions of operating systems and load testing tools whenever you want, without needing to liaise with a third-party provider. This option can come in handy if you routinely use non-standard configurations or legacy software. If you&amp;rsquo;re also doing other types of non-functional testing, like security testing, it may be easier to change the configuration on on-premise machines.&lt;/p&gt;
&lt;h4 id="greater-data-security"&gt;Greater data security&lt;/h4&gt;
&lt;p&gt;The question of cloud vs. on-premises may be a business decision. Some industries or businesses with stringent compliance and regulatory requirements may have no choice but to run tests on-premise because owning the load generators also means controlling who has access to the data that&amp;rsquo;s on them. An on-premise environment lets you lock down access if necessary. With cloud load testing, you&amp;rsquo;d need to have security arrangements with the cloud provider that you choose so that any test data or identifying client information seen or accessed by your test scripts remains confidential.&lt;/p&gt;
&lt;h4 id="lower-cost-and-lower-barrier-to-entry-in-some-situations"&gt;Lower cost and lower barrier to entry in some situations&lt;/h4&gt;
&lt;p&gt;If you don&amp;rsquo;t intend to run too many users of your test script and you already have a spare machine or two at the office, on-premise load testing may be the easiest and most cost-effective way to get started. This way, you avoid having to request the budget for a new tool and needing to train your team on a new interface.&lt;/p&gt;
&lt;h4 id="fewer-variations-due-to-latency"&gt;Fewer variations due to latency&lt;/h4&gt;
&lt;p&gt;Controlling your own load generators means controlling the network they run on. If your corporate network is generally stable, and your test scenarios don&amp;rsquo;t involve testing from different geographical locations, reported response times (and user experience) more closely reflect server processing time rather than latency and travel time.&lt;/p&gt;
&lt;h3 id="disadvantages-of-on-premise-load-testing"&gt;Disadvantages of on-premise load testing&lt;/h3&gt;
&lt;p&gt;‍
&lt;img src="https://nicolevanderhoeven.com/assets/20200430-03.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h4 id="higher-cost-in-some-situations"&gt;Higher cost in some situations&lt;/h4&gt;
&lt;p&gt;Maintaining your own load generators comes with a higher up-front cost, which may be a showstopper for smaller projects or companies. The more concurrent users you require for your load test, the more machines you need, the more licenses you need for your testing tool (unless it&amp;rsquo;s open source), and the longer it takes to recover your investment.&lt;/p&gt;
&lt;h4 id="geographical-limitations"&gt;Geographical limitations&lt;/h4&gt;
&lt;p&gt;Since all your machines are in one place, you can&amp;rsquo;t accurately simulate the user experience for customers who might access your web app from another location. You also can&amp;rsquo;t fully test the effect of CDNs and other tools that your application might employ to keep response times low. Web apps that can be accessed by anyone on the internet might require performance tests that take into account customers from different regions.&lt;/p&gt;
&lt;h4 id="more-setup"&gt;More setup&lt;/h4&gt;
&lt;p&gt;You need to do all the setup yourself for both hardware and software components. You also need a way to coordinate your test, so that load generators start and end when you want them to. You may want to divert some resources towards environment management.&lt;/p&gt;
&lt;h4 id="less-flexibility"&gt;Less flexibility&lt;/h4&gt;
&lt;p&gt;With on-premise load generators, you can&amp;rsquo;t add, remove, or scale up nodes on the fly according to your test— something cloud load testing excels at.&lt;/p&gt;
&lt;h2 id="on-premise-load-testing-best-practices"&gt;On-premise load testing best practices&lt;/h2&gt;
&lt;p&gt;The environment you&amp;rsquo;re testing in is especially important when you&amp;rsquo;re using your own infrastructure. Here are some best practices for a performance testing strategy that involves on-premise load generation.&lt;/p&gt;
&lt;h3 id="run-baseline-tests"&gt;Run baseline tests&lt;/h3&gt;
&lt;p&gt;Treat load testing like a scientific experiment. Before you begin changing variables to improve performance, spend some time figuring out your test configuration so that it&amp;rsquo;s repeatable. Pay attention to things like workloads, business processes, the number of users you&amp;rsquo;re running per load generator, test environment configuration values, script pacing and think time set in your testing tool, and test script transactions. Make sure that the resource utilization on your machines remains healthy during a test; otherwise, you&amp;rsquo;re load testing the load generators themselves rather than your application servers.&lt;/p&gt;
&lt;p&gt;Then, run a few tests against your application servers without making any changes. This way, you create a &amp;ldquo;control group&amp;rdquo; or control test against which to compare future tests.&lt;/p&gt;
&lt;p&gt;This process is called &lt;em&gt;baselining&lt;/em&gt; your application performance.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200430-04.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Flood&amp;rsquo;s Project View lets you track an application&amp;rsquo;s performance over time and compare recent tests to a baseline&lt;/em&gt;
‍&lt;/p&gt;
&lt;h3 id="consider-using-containers-or-virtual-machine-images"&gt;Consider using containers or virtual machine images&lt;/h3&gt;
&lt;p&gt;Along the lines of reducing variables, it&amp;rsquo;s especially important to use load generators that are as identical as you can make them. You can manually check that you&amp;rsquo;re installing the same software and tools on each one, or you can do as cloud providers do and automate this process.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easy to miss software that are necessary for monitoring or the load testing tool you&amp;rsquo;re using. For example, JMeter and Gatling require certain versions of Java. Test automation or browser-level load testing tools might require web browsers.&lt;/p&gt;
&lt;p&gt;You can use &lt;a href="https://www.docker.com/resources/what-container"&gt;containers such as Docker&lt;/a&gt; to create a standalone package of everything you need for your test. Alternatively, if you&amp;rsquo;re using virtual machines, you can also create a full system image that you can use for every virtual machine.&lt;/p&gt;
&lt;p&gt;The difference between the two is that a container lets you be more granular about what is included in the package that you then deploy to all your load generators. Virtual machines include hardware components that you can set, whereas containers are software-only, decreasing package sizes.&lt;/p&gt;
&lt;p&gt;Using containers or images allows you to keep your load generators as identical as possible (in terms of hardware and software) so that you can assume similar results from each one.&lt;/p&gt;
&lt;p&gt;‍‍&lt;img src="https://nicolevanderhoeven.com/assets/20200430-05.jpg" alt="" loading="lazy" decoding="async"&gt;
‍&lt;/p&gt;
&lt;h3 id="dont-neglect-monitoring"&gt;Don&amp;rsquo;t neglect monitoring&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s easy to forget that your application servers aren&amp;rsquo;t the only ones that need monitoring. Your load generators need monitoring as well, so that they don&amp;rsquo;t end up being the bottleneck in your tests. Here&amp;rsquo;s &lt;a href="https://flood.io/blog/load-testing-tutorial-server-monitoring"&gt;a starter list of server metrics&lt;/a&gt; that you should consider monitoring.&lt;/p&gt;
&lt;p&gt;You might also consider how you can integrate &lt;a href="https://stackify.com/mistakes-evaluating-application-performance-management/"&gt;performance management&lt;/a&gt; tools you already use into your monitoring strategy.&lt;/p&gt;
&lt;h3 id="spend-time-on-a-framework-for-gathering-data"&gt;Spend time on a framework for gathering data&lt;/h3&gt;
&lt;p&gt;One of the biggest challenges in on-premise load testing is figuring out how to get data from your tests. Each machine runs your test script independently of the others, so at the end of your test, you&amp;rsquo;re likely to end up with several results files. After you download these off every machine, how do you combine them into a single summary?&lt;/p&gt;
&lt;p&gt;Manually collating results is an option, but it&amp;rsquo;s a tedious one. You can copy all the files into one directory and then concatenate them so that you end up with one giant one. Depending on the size of those results files and how long you ran the test, however, this may not be feasible. For larger files, you might consider aggregating the data and increasing the granularity, so that instead of seeing raw response time for every transaction, you only see the average response time for every 15 seconds. You can use a tool like &lt;a href="https://www.elastic.co/logstash"&gt;Logstash&lt;/a&gt; for this.&lt;/p&gt;
&lt;p&gt;It takes some time and technical expertise to set up a framework for results gathering that works for your purposes, especially if you want to go a step further and have real-time results.&lt;/p&gt;
&lt;p&gt;Of course, you can also skip this part by just using the framework we&amp;rsquo;ve already set up on Flood. Tim Koopmans, our co-founder, created the first prototype of what would become Flood specifically because he got tired of having to set up this framework for every engagement.&lt;/p&gt;
&lt;h2 id="how-to-run-an-on-premise-load-test-with-flood-agent"&gt;How to run an on-premise load test with Flood Agent&lt;/h2&gt;
&lt;p&gt;‍
The easiest way to get started with an on-premise load test is by using &lt;a href="https://flood.io/blog/introducing-flood-agent-standalone-infrastructure-for-load-testing"&gt;Flood Agent&lt;/a&gt;. Flood Agent is an executable that you can install onto a machine that effectively turns it into a full-fledged load generator. Then, you can start a test from the Flood web interface and have your test script start running on your machines.&lt;/p&gt;
&lt;p&gt;Agent takes care of a few typical problems we&amp;rsquo;ve discussed so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses containers to ensure that load generators are identically provisioned.&lt;/li&gt;
&lt;li&gt;It automatically gathers CPU, memory, and network metrics on load generators.&lt;/li&gt;
&lt;li&gt;It coordinates the execution of the load test so that you only need to start it from one central place.&lt;/li&gt;
&lt;li&gt;It enables real-time monitoring of results while the test is running.&lt;/li&gt;
&lt;li&gt;It aggregates and collates all the results for you into a dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We think Agent addresses pain points that we&amp;rsquo;ve seen over and over again in our own on-premise performance testing projects as well as in our customers&amp;rsquo;. Agent is a &lt;a href="https://github.com/flood-io/flood-agent/releases/latest"&gt;free download&lt;/a&gt; and works with &lt;a href="https://guides.flood.io/scripting-and-tools/choosing-a-tool"&gt;all performance testing tools that Flood supports&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;On-premise load testing is still a valid option despite the growth in popularity of cloud-based infrastructure, and it has its advantages and disadvantages. This type of load testing trades a little bit of flexibility for increased data security and control, which is a tradeoff that some companies may be willing to make.&lt;/p&gt;
&lt;p&gt;On-premise load testing has traditionally included a lot of tedious setup and maintenance. Still, you can reduce maintenance effort by creating an internal load testing framework upfront that automates coordination, monitoring, and results collection. Using Flood Agent can mitigate time spent on this initial setup.&lt;/p&gt;</description></item><item><title>Ask a Flooder 13: How do you do mobile load testing?</title><link>https://nicolevanderhoeven.com/blog/20200428-aaf13/</link><pubDate>Tue, 28 Apr 2020 23:21:08 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200428-aaf13/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-13-how-do-you-do-mobile-load-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-13-how-do-you-do-mobile-load-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;How do you load test a native mobile app? I discuss the general approach to mobile load testing, what a device farm is, and why a device farm isn&amp;rsquo;t necessary for load testing. I also discuss the best mobile load testing tools to use.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/y_rRu0cDey4?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Or listen to the audio version here:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/2fevtJd7pPIAUDkMpdnzkV" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;p&gt;Hi everyone, Nicole here again, back with another Ask a Flooder, and today&amp;rsquo;s question is:&lt;/p&gt;
&lt;h2 id="how-do-you-do-mobile-load-testing"&gt;How do you do mobile load testing?&lt;/h2&gt;
&lt;p&gt;Now, this question came from a customer who was under the misconception that in order to do mobile load testing, they would need to use what we call a device farm. A device farm is a collection of different kinds of mobile phones. You would ideally have the most popular makes of mobile phones (the hardware models) and the operating systems and system versions. You can imagine that this can very easily get a little out of hand to coordinate because of fragmentation. There are just so many devices out on the market, and then there&amp;rsquo;s the question of how you actually generate the load from that.&lt;/p&gt;
&lt;p&gt;You would somehow need some sort of test automation script that was running and then coordinate os that they all run at the same time, and you would use that as a load test. As you can imagine, whether you use real devices or simulators, this kind of thing would get complicated and expensive very quickly. But the good news is that it&amp;rsquo;s unnecessary if it&amp;rsquo;s load testing that you want to do. Now I say that because, for functional testing or compatibility testing, you ARE going to want to test your application on a variety of different browsers. But with load testing, you&amp;rsquo;re really more concerned about the back end part of the equation. You want to generate load on your application servers. In reality, most mobile apps work by sending API requests to that server, usually by some sort of restful API. Luckily, API load testing has been around for a while, so we do have some industry-tested ways to run this.&lt;/p&gt;
&lt;h2 id="what-are-the-best-tools-for-api-load-testing"&gt;What are the best tools for API load testing?&lt;/h2&gt;
&lt;p&gt;Essentially, you&amp;rsquo;re going to want to test protocol-level tools. That means that you don&amp;rsquo;t want to be using browser-level tools in which you&amp;rsquo;re using scripts to drive a full browser. All you really need is something that sends requests on the protocol-level. That&amp;rsquo;s great because it&amp;rsquo;s actually way more efficient.&lt;/p&gt;
&lt;p&gt;You have two really good open-source options for this, JMeter and Gatling. I&amp;rsquo;ve already gone a little bit into the differences between these two in my previous video on how you choose a tool for load testing, which I will leave a link to.&lt;/p&gt;
&lt;p&gt;And here are some other resources that will help you get started with mobile load testing. I also have another video about how to record network traffic from a native mobile app on JMeter, which sounds a little complicated, but I promise it&amp;rsquo;s not. In that video, I go over the steps with you, and I&amp;rsquo;m actually doing it in real-time with you as well. I show you the screen recording from my mobile and from JMeter on my laptop, so you can always pause and watch it at your own pace.&lt;/p&gt;
&lt;p&gt;The second thing is that I already wrote a blog post, which is sort of a guide for mobile load testing, which goes into the steps into way more detail than I can here.&lt;/p&gt;
&lt;p&gt;And lastly, I have written blog posts about two real-life examples of mobile load testing. The first one was the Iowa Caucus Reporter app. I ran through it from the workload modeling to the scripting and then the actual execution. More recently, I also wrote about the Robinhood app. Be sure to check out the links in the description to find out more.&lt;/p&gt;
&lt;p&gt;Until next time, happy flooding!&lt;/p&gt;</description></item><item><title>Browser-based load testing in 2020</title><link>https://nicolevanderhoeven.com/blog/20200423-browser-based-load-testing-2020/</link><pubDate>Thu, 23 Apr 2020 16:48:09 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200423-browser-based-load-testing-2020/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/browser-based-load-testing-in-2020"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/browser-based-load-testing-in-2020"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We &lt;a href="https://flood.io/blog/level-up-your-load-testing-with-browser-level-users"&gt;first announced&lt;/a&gt; our open-source browser-based load testing tool based on Puppeteer, &lt;a href="https://element.flood.io/"&gt;Flood Element&lt;/a&gt;, in February 2018. Despite our decision to make it an entirely separate tool from Flood, we&amp;rsquo;ve enjoyed using it so much as a team that several of us have written about it:&lt;/p&gt;
&lt;p&gt;Tim Koopmans: &lt;a href="https://flood.io/blog/load-testing-for-devops-with-browser-level-users-blu"&gt;Load testing for DevOps&amp;hellip; with Browser Level Users (BLU)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Kevin Dunne: &lt;a href="https://flood.io/blog/a-new-approach-to-load-testing-with-browser-level-users"&gt;A new approach to load testing with browser level users&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;me: &lt;a href="https://flood.io/blog/prepare-for-viral-videos-with-flood-element"&gt;Prepare for viral videos with Flood Element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Lachie Cox: &lt;a href="https://flood.io/blog/why-you-should-load-test-with-browsers"&gt;Why should you load test with real browsers?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;our friend Eduardo Riol, as translated by Antonio Jimenez: &lt;a href="https://flood.io/blog/selenium-vs-puppeteer-for-test-automation-is-a-new-leader-emerging"&gt;Puppeteer vs. Selenium for test automation: is a new leader emerging?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Clearly, we&amp;rsquo;re huge fans of load testing a web application with real browsers. But does the wider software testing industry share that sentiment?&lt;/p&gt;
&lt;h2 id="browser-based-load-testing-in-2020"&gt;Browser-based load testing in 2020&lt;/h2&gt;
&lt;p&gt;Browser-level load testing is still pretty new, and there aren&amp;rsquo;t too many providers offering it right now. Luckily, we just so happen to have access to a database of load tests run on Flood since 2013. Here&amp;rsquo;s a bar chart visualizing data we&amp;rsquo;ve collected about the load testing tools flooders have used (we support Selenium, JMeter, Gatling, and Element).&lt;/p&gt;
&lt;h3 id="load-tests-executed-on-flood-over-time-grouped-by-load-testing-tool"&gt;Load tests executed on Flood over time, grouped by load testing tool&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200423-01.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="browser-level-load-testing-is-increasing-in-popularity"&gt;Browser-level load testing is increasing in popularity&lt;/h3&gt;
&lt;h4 id="in-2020-almost-35-of-the-load-tests-run-on-flood-are-browser-based-load-tests"&gt;In 2020, almost 35% of the load tests run on Flood are browser-based load tests.&lt;/h4&gt;
&lt;p&gt;While the total number of load tests run across all tools increased across the board, the percentage of tests that utilized Selenium and Element, as compared to JMeter and Gatling, has also increased from 2018. Both Selenium and Element are browser-based load testing tools.&lt;/p&gt;
&lt;p&gt;By contrast, JMeter, a protocol-level load testing tool which previously enjoyed a 70% share of the traffic on Flood, now accounts for only a bit over 50% of usage. JMeter&amp;rsquo;s share of the protocol-level pie is higher than Gatling&amp;rsquo;s, but the percentage of usage for both tools appears to be contracting when compared to that of browser-based tools.&lt;/p&gt;
&lt;h3 id="element-use-is-increasing-faster-than-selenium-use"&gt;Element use is increasing faster than Selenium use&lt;/h3&gt;
&lt;p&gt;Despite the fact that Selenium has been around longer, &lt;strong&gt;25% of all floods today are run using Element&lt;/strong&gt;. This is a significant increase in just two years, and one that bodes well for the future. While we&amp;rsquo;re still fans of Selenium and still support it, one reason we see for the dominance of Element in today&amp;rsquo;s market is resource efficiency.&lt;/p&gt;
&lt;p&gt;In our baseline tests, we&amp;rsquo;ve seen that Element can run from 20-50 users per Flood node (an &lt;a href="https://aws.amazon.com/blogs/aws/m5-the-next-generation-of-general-purpose-ec2-instances/"&gt;AWS m5.xlarge instance&lt;/a&gt;), whereas Selenium can only run up to 5. This leads to some significant cost savings, as running the same number of users with Element requires fewer load generators than with Selenium.&lt;/p&gt;
&lt;p&gt;Another reason might be the fact that Element uses Puppeteer in the background. Puppeteer, developed by Google, understandably shines when driving Google Chrome (and recently, Firefox). Puppeteer load testing is growing in popularity due to the wealth of metrics it has access to.&lt;/p&gt;
&lt;h2 id="how-browser-based-load-testing-works"&gt;How browser-based load testing works&lt;/h2&gt;
&lt;p&gt;Traditional load testing is done on the protocol level by sending messages directly to the web server. These messages are sent without simulating the application interface, and the responses that are received from the server are not visualized, either. &amp;ldquo;Users&amp;rdquo; in protocol-level load testing scripts are actually threads, with each one sending a list of messages at predefined intervals, and we refer to these users as Protocol-Level Users (PLU). JMeter and Gatling use PLU to generate load.&lt;/p&gt;
&lt;p&gt;The screenshot below shows what one request for a single action might look like in a JMeter test plan.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200423-02.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Example of an HTTP request with parameters on JMeter&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In browser-based load testing, real browser instances are opened on load generators, and the load testing tool plays the part of a real user in navigating the web application using the GUI. We refer to the virtual users in browser-based load testing as Browser-Level Users (BLU).&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of the same action in the JMeter example above, but this time in an Element test script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Step 1&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Click Start button
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;startButton&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;findElement&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;.btn-default&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;startButton&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;click&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This version simply describes the button to be found and clicked, whereas the protocol-level version needs to be more precise in what exactly is passed to the server.&lt;/p&gt;
&lt;p&gt;Both types of load testing tools can apply the same load on an application (as long as it supports both methods— see the next section). The main difference is in how the load is generated.&lt;/p&gt;
&lt;p&gt;A protocol-level script will contain instructions on which messages to send, which protocols to use, which headers to include, and other information that a typical end-user would not see.&lt;/p&gt;
&lt;p&gt;A browser-level script will contain instructions on how to &lt;em&gt;interact&lt;/em&gt; with the application: which buttons to click, what to type in text fields, which links to follow, and other on-screen elements that an end-user would be familiar with. This leads us to one of the big advantages of browser-based load testing.&lt;/p&gt;
&lt;h2 id="pros-and-cons-of-browser-based-load-testing"&gt;Pros and cons of browser-based load testing&lt;/h2&gt;
&lt;h3 id="pros"&gt;Pros&lt;/h3&gt;
&lt;h4 id="simple-scripting-or-no-scripting-at-all"&gt;Simple scripting— or no scripting at all&lt;/h4&gt;
&lt;p&gt;Protocol-level tools require in-depth knowledge about how an application works, what format it expects requests to be in, and how it responds. On the browser-level, you don&amp;rsquo;t need to know any of that. When scripting a login process, for instance, you just need to tell the script to type a username and password in the right fields and then click Log In. The script doesn&amp;rsquo;t need to know that the form submits an encrypted request using the Auth0 API (something you&amp;rsquo;d need to know for a protocol-level script).&lt;/p&gt;
&lt;p&gt;In some cases, you might be able to get away with using something like our &lt;a href="https://flood.io/blog/record-load-tests-in-flood-element-using-qtest-explorer"&gt;qTest Explorer plugin&lt;/a&gt; to record a business process, generating a script without any scripting at all. Even when you have to write or modify the script, though, it&amp;rsquo;s a lot less intimidating than most protocol-level scripts.&lt;/p&gt;
&lt;p&gt;They&amp;rsquo;re also easier to troubleshoot. In addition to opening a real browser instance on your computer when you run a script locally, Element can also display this information on the console (shown here on iTerm):&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200423-03.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;em&gt;Screenshots upon execution on Element using iTerm&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;‍This allows you to understand what&amp;rsquo;s going on in the script and in the application, letting you detect bottlenecks faster.&lt;/p&gt;
&lt;h4 id="reduced-test-complexity"&gt;Reduced test complexity&lt;/h4&gt;
&lt;p&gt;The streamlined requirements for scripting lead to a straightforward test. There&amp;rsquo;s no chance of getting request parameters or values wrong.&lt;/p&gt;
&lt;h4 id="ability-to-test-entire-stack-from-the-user-perspective"&gt;Ability to test entire stack from the user perspective&lt;/h4&gt;
&lt;p&gt;Unlike testing APIs, you don&amp;rsquo;t need to decide which server you want to hit or make sure you have the appropriate requests to hit all the servers. A browser-level test script interacts with page elements like a real user would, so all underlying application servers are tested. Pair this with &lt;a href="https://guides.flood.io/infrastructure/demand-infrastructure/advantages-of-testing-in-the-cloud"&gt;cloud-based infrastructure on Flood&lt;/a&gt;, and you&amp;rsquo;ve got a compact package for testing your application.&lt;/p&gt;
&lt;h4 id="capable-of-testing-any-user-behavior"&gt;Capable of testing any user behavior&lt;/h4&gt;
&lt;p&gt;Protocol-level load test tools struggle with some types of applications, such as single-page apps (SPAs), where a lot of the processing is done on the client side. Browser-level load testing tools shine here, because scripted interactions for these apps are no different for SPAs than other web apps.&lt;/p&gt;
&lt;h4 id="record-network-and-user-interaction-times-for-front-end-optimization"&gt;Record network and user interaction times for front-end optimization&lt;/h4&gt;
&lt;p&gt;Browser-level tools show you response times from the user&amp;rsquo;s perspective: how long after a user clicks the log in button does the user see his or her account balance? This metric is a practical one that may differ wildly from the traditional response times that a protocol-level tool reports. Measuring real user experience is a great value add for your test.&lt;/p&gt;
&lt;h4 id="easier-to-maintain"&gt;Easier to maintain&lt;/h4&gt;
&lt;p&gt;Lower script complexity means that a browser-based test script is more resilient to changes in the codebase. A change in the underlying requests sent will not require a change in the test script, making it perfect for regression testing.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also easier to pass on to other members of the team due to high readability. Here&amp;rsquo;s an example of how to select a value from a dropdown menu in Element:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;selectByValue&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;#challenger_age&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;34&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s easy to see that the script is looking for an element with the ID &amp;ldquo;challenger_age&amp;rdquo; and selecting the value &amp;ldquo;34&amp;rdquo;. This step could correspond to dozens of requests that need to be scripted if testing on the protocol level, which makes those scripts harder to understand without a proper handover.&lt;/p&gt;
&lt;h3 id="cons"&gt;Cons&lt;/h3&gt;
&lt;p&gt;Browser-based load testing tools have a higher CPU and memory footprint than protocol-based tools. Test scripts in JMeter and Gatling send messages to and record responses from a server without really needing to parse responses. By contrast, browser-based tools parse the server response in order to access the Domain Object Model (DOM) elements on the page.&lt;/p&gt;
&lt;p&gt;Using real browsers might be closer to the real user experience, but it also takes its toll on node resources. It is possible to run in headless mode, where browsers are started &amp;ldquo;invisibly&amp;rdquo; without actually graphically rendering them. However, even then, a browser-level tool can run fewer concurrent users on a node than a protocol-level tool.&lt;/p&gt;
&lt;h2 id="best-tools-and-applications-for-browser-based-load-testing"&gt;Best tools and applications for browser-based load testing&lt;/h2&gt;
&lt;p&gt;While other commercial tools exist for this, we&amp;rsquo;re partial to open source tools at Flood.&lt;/p&gt;
&lt;h3 id="selenium"&gt;Selenium&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200423-04.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Selenium load testing is appealing because Selenium is already well-known for test automation and functional testing, and a lot of companies already have full test suites in Selenium. The possibility of reusing those resources and just running them at scale as load tests is, by far, the most compelling reason to use Selenium.&lt;/p&gt;
&lt;h3 id="puppeteer"&gt;Puppeteer&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200423-05.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Google&amp;rsquo;s just getting started with Puppeteer, and the amount of metrics it exposes is exciting: it seems like anything in Chrome Developer Tools can be accessed by Puppeteer. Imagine running a load test on ONE tool that gives you statistics about back-end performance (response times, the effect of bandwidth restrictions, resources returning errors) AND front-end performance (client-side script console logs, render times, time to first paint, etc). It&amp;rsquo;s hard to imagine a future where this ISN&amp;rsquo;T a valuable tool in the performance arsenal.&lt;/p&gt;
&lt;p&gt;Under Puppeteer, of course, is &lt;a href="https://element.flood.io/"&gt;Element&lt;/a&gt;— our load-flavored version of Puppeteer.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200423-06.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="is-protocol-based-load-testing-dead"&gt;Is protocol-based load testing dead?&lt;/h2&gt;
&lt;p&gt;No. Testing on the protocol-level may have its drawbacks, but it&amp;rsquo;s popular for a reason. If you&amp;rsquo;re testing mobile apps or API endpoints, protocol-level testing tools allow you to focus on hitting what you want to test and only what you want to test. Sometimes you don&amp;rsquo;t want to test the whole stack. It really depends on your test scenarios.&lt;/p&gt;
&lt;p&gt;Plus, protocol-level tools tend to be really resource efficient. We&amp;rsquo;ve been able to easily run about 1000 users per Flood node with JMeter and Gatling, easily outstripping browser-level tools. It makes sense, since JMeter and Gatling don&amp;rsquo;t need to start browser instances to generate load.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s significantly cheaper to generate very high loads with protocol-level tools. This efficiency makes protocol-level tools a good choice for high concurrency load testing and especially stress testing.&lt;/p&gt;
&lt;h2 id="what-are-we-doing-to-support-blu"&gt;What are we doing to support BLU?&lt;/h2&gt;
&lt;p&gt;Clearly, our usage metrics show that browser-based load testing is on the rise, and we&amp;rsquo;re planning to ramp up our offerings in this area while also maintaining support for protocol-level tools. Here&amp;rsquo;s what you can look forward to from us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New releases
&lt;ul&gt;
&lt;li&gt;We just released &lt;a href="https://element.flood.io/docs/1.0/install"&gt;version 1.2.3 of Element&lt;/a&gt; a few weeks ago, which addresses some bugs and improve stability for certain actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dedicated Element team
&lt;ul&gt;
&lt;li&gt;We&amp;rsquo;ve put together a team of developers who will be focusing on improving Element exclusively. Despite Element being an open source tool, we&amp;rsquo;re putting our own resources behind it to improve it faster.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Partnering with consulting services
&lt;ul&gt;
&lt;li&gt;It can be difficult to get started on a new tool, and Element is no exception. While we provide a load testing platform to run Element scripts, some companies want help to create the scripts in the first place. To this end, we&amp;rsquo;ve partnered with expert consultants who have already been using Element with great success: &lt;a href="https://foulkconsulting.com/"&gt;Foulk Consulting&lt;/a&gt; and &lt;a href="https://www.planittesting.com/"&gt;Planit Testing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;When choosing a performance testing tool, it&amp;rsquo;s important to consider the advantages and disadvantages of protocol-level and browser-level testing tools. We think both types of tools deserve to be in a solid performance and load testing strategy. They can also be used in tandem in a hybrid load testing approach, improving overall application performance and scalability.&lt;/p&gt;
&lt;p&gt;In 2018, we predicted that testing web-based applications with real browsers would increase in popularity, so we&amp;rsquo;re thrilled to see our theory validated in 2020. Browser-based load testing is increasing in usage, and our open source tool, Element, is leading the charge. It&amp;rsquo;s exciting to be at the forefront of this revolution, and we intend to increase support for load testing with real browsers even more this year.&lt;/p&gt;</description></item><item><title>Ask a Flooder 12: Puppeteer vs Selenium: which one is better for load testing?</title><link>https://nicolevanderhoeven.com/blog/20200421-aaf12/</link><pubDate>Tue, 21 Apr 2020 17:36:28 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200421-aaf12/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-12-puppeteer-vs-selenium-which-one-is-better-for-load-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-12-puppeteer-vs-selenium-which-one-is-better-for-load-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When it comes to software testing, the debate of Puppeteer vs Selenium is a hot one. Nicole tackles it here to determine which one is better for load testing in particular. Spoiler: The answer is still &amp;ldquo;it depends&amp;rdquo;.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/K61W7ivhsuE?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Or catch the audio version here:&lt;/p&gt;
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/76u25lI7sFx5SwgbsFLU5Z" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;p&gt;Hi everyone, Nicole here again, back for another Ask a Flooder, and today, the question is: &amp;ldquo;Puppeteer vs. Selenium: which one is better for load testing?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re actually getting this quite a bit now as Puppeteer grows in popularity, but first, let&amp;rsquo;s discuss the similarities. They&amp;rsquo;re tools that you can use to drive browsers. The work by identifying and interacting with elements on a website in the same way that a user would. They are also both open-source, and both can be used for load testing. This is a great strategy because instead of writing tests for an automation suite and then tests for load testing, you can just write the one test suite and re-use it, so it saves a lot of resources in the end. To answer this question, I&amp;rsquo;m going to focus on load testing because that&amp;rsquo;s what my experience is in.&lt;/p&gt;
&lt;h2 id="difference-between-selenium-and-puppeteer"&gt;Difference between Selenium and Puppeteer&lt;/h2&gt;
&lt;h3 id="pros-and-cons-of-selenium"&gt;Pros and cons of Selenium&lt;/h3&gt;
&lt;p&gt;Now for the difference. Selenium supports more browsers than does Puppeteer. It supports Chrome, Firefox, IE, Safari, and Opera, so there are a lot more options to choose from depending on your application.&lt;/p&gt;
&lt;p&gt;Selenium is also what I would call automation-first. It was built with automation in mind, which means it&amp;rsquo;s very good at that. It&amp;rsquo;s very robust, and if you have a complicated test scenario, Selenium is an excellent choice.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also been around for much longer than Puppeteer, which is important because that means it&amp;rsquo;s had more time to build up a community. There are way more resources and tutorials for Selenium than for Puppeteer.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also, unfortunately, very resource-intensive. That&amp;rsquo;s not something that you would typically care too much about from a test automation perspective, but when you&amp;rsquo;re running thousands of users on the cloud, it IS important to keep in mind that Selenium, just because of how it&amp;rsquo;s built and its complexity, can only run about five users per node. We&amp;rsquo;ve found that out at Flood, after baselining specifically an AWS m5.xlarge instance. So you can look at the specs of that and compare it with the machines that you&amp;rsquo;re wanting to run Selenium on, to see how that would equate for you.&lt;/p&gt;
&lt;h3 id="pros-and-cons-of-puppeteer"&gt;Pros and cons of Puppeteer&lt;/h3&gt;
&lt;p&gt;Puppeteer is a NodeJS library developed by Google, which means it plays very well with Chrome. There are a whole lot of things that you have access to with Puppeteer, and if you&amp;rsquo;re using it with Chrome, pretty much anything in Chrome, including rendering times, you can see and access with a Puppeteer script. So that&amp;rsquo;s a huge advantage.&lt;/p&gt;
&lt;p&gt;Unfortunately, that also means it supports fewer browsers. Obviously, it supports Chrome very well, and they recently came out with support for Firefox, although they do have plans to incorporate support for more browsers in the future.&lt;/p&gt;
&lt;p&gt;One of Puppeteer&amp;rsquo;s advantages is that we at Flood actually created a tool on top of Puppeteer. It&amp;rsquo;s also open-source. It&amp;rsquo;s called Flood Element, and Element is built to be performance-first because obviously, that&amp;rsquo;s what we had in mind when we were building it. It takes everything that&amp;rsquo;s good about Puppeteer and makes it really easy to set parameters that you normally would expect to need for load testing, like users and ramp-up.&lt;/p&gt;
&lt;p&gt;This also means that it&amp;rsquo;s way less resource-intensive. I said that with Selenium, you can run up to five users on an AWS m5.xlarge instance, and for the same instance type, you can run, from our experiments, anywhere from 30-50 Element scripts/users. That is a huge saving when you&amp;rsquo;re running a really large-scale test.&lt;/p&gt;
&lt;h2 id="should-you-choose-selenium-or-puppeteer-for-load-testing"&gt;Should you choose Selenium or Puppeteer for load testing?&lt;/h2&gt;
&lt;p&gt;I would say that if you already have a large Selenium automation suite, if you have a complex test scenario, or if you aren&amp;rsquo;t running that many users for your load test, then Selenium is a great choice. If you ARE running thousands of users, though, and resource efficiency, and therefore cost efficiency, is really important to you, then choose Element.&lt;/p&gt;
&lt;p&gt;My advice is always to try both. If you have the time, do a proof of concept with both of them. That way, your whole team can better understand what the pros and cons are for each one and decide which one is best going forward. Or maybe you don&amp;rsquo;t even decide on one. At Flood, we support both of them at the same price, so it depends on what you prefer.&lt;/p&gt;
&lt;p&gt;Until next time, happy flooding!&lt;/p&gt;</description></item><item><title>Introducing Flood Agent: standalone infrastructure for load testing</title><link>https://nicolevanderhoeven.com/blog/20200407-introducing-flood-agent/</link><pubDate>Tue, 07 Apr 2020 19:03:42 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200407-introducing-flood-agent/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/introducing-flood-agent-standalone-infrastructure-for-load-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/introducing-flood-agent-standalone-infrastructure-for-load-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Working from home during this pandemic and wondering how to start load tests from the office? Today, we&amp;rsquo;re officially introducing Flood Agent, a way to turn your own physical or virtual machines into load generators that you can control from anywhere with an internet connection.&lt;/p&gt;
&lt;h2 id="tldr"&gt;TL;DR:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Installing Flood Agent on a machine lets you start load tests on it remotely from Flood—even if it&amp;rsquo;s within a corporate VPN.&lt;/li&gt;
&lt;li&gt;Flood Agent is a simple binary that you can install on Linux, macOS, and Windows machines.&lt;/li&gt;
&lt;li&gt;Once you&amp;rsquo;ve installed Flood Agent, your machines show up on the Flood web app to use as load generators or grids.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="boldly-going-where-no-one-has-gone-before"&gt;Boldly going where no one has gone before&lt;/h2&gt;
&lt;p&gt;Load testing within your company&amp;rsquo;s VPN and trying to coordinate it from home is like being on a maintenance pod trying to contact your starship, but a Mazarite ship is broadcasting a jamming signal, so comms are down. That Mazarite ship is your company&amp;rsquo;s firewall, and it can make testing difficult.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200407-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The easiest way around this is to provision load generators in the cloud. However, because your application servers are still behind a firewall, you&amp;rsquo;d need to convince IT security to &lt;a href="https://guides.flood.io/test-execution/infrastructure/static-ip-addresses#hosted-grids"&gt;whitelist the IP addresses&lt;/a&gt; of the load generators. Depending on how tight security policies are, this may not be an option even in this new world of remote working.&lt;/p&gt;
&lt;p&gt;Flood Agent is a way to start load tests on Flood from the load generators you already have. It&amp;rsquo;s a cross-platform agent that you can install on your regular load generators, allowing you to control them on Flood.&lt;/p&gt;
&lt;p&gt;‍‍&lt;img src="https://nicolevanderhoeven.com/assets/20200407-02.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Flood Agent is kind of like the &lt;a href="https://en.wikipedia.org/wiki/Borg#Borg_Collective"&gt;Borg Collective&lt;/a&gt;. Each machine that you install Flood Agent on is assimilated into the Borg. A hive mind (you, on the Flood site) controls all Borgs, coordinating test execution with robotic efficiency.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://media.giphy.com/media/J6P7vFEQ6PcgE/giphy.gif" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Check out this video to follow along as I walk you through the process of setting up Flood Agent, or you can keep reading for the text version.&lt;/p&gt;
&lt;p&gt;‍&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/6VFn96aKSU8?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
‍&lt;/p&gt;
&lt;h2 id="installing-and-configuring-flood-agent"&gt;Installing and configuring Flood Agent&lt;/h2&gt;
&lt;p&gt;Flood Agent is a free binary, and it runs on Linux, Mac, or Windows machines. You can &lt;a href="https://github.com/flood-io/flood-agent/releases/latest"&gt;download the appropriate version&lt;/a&gt; for your operating system and then follow the installation instructions here for more information:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://guides.flood.io/infrastructure/standalone-infrastructure/getting-started-on-linux"&gt;Getting started on Linux&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://guides.flood.io/infrastructure/standalone-infrastructure/getting-started-on-macos"&gt;Getting started on macOS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://guides.flood.io/infrastructure/standalone-infrastructure/getting-started-on-windows"&gt;Getting started on Windows&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After installing Flood Agent, you can configure it according to how you want to use it. Start Flood Agent in your terminal and use the configure functionality. Here&amp;rsquo;s what that command looks like on macOS, but you can follow the links above for other operating systems:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./flood-agent configure
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="linking-your-flood-account"&gt;Linking your Flood account&lt;/h3&gt;
&lt;p&gt;The command above yields a prompt for a token like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200407-03.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This token allows Flood to run load tests on the load generator that you&amp;rsquo;ve installed Flood Agent in. To get the token, go to the &lt;a href="https://app.flood.io/account/api"&gt;API Access page&lt;/a&gt; on your Flood account (&lt;a href="https://app.flood.io/sign-up"&gt;you already have one, don&amp;rsquo;t you?&lt;/a&gt;) and click REVEAL token.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200407-04.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;You should see a string of characters like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200407-05.jpg" alt="" loading="lazy" decoding="async"&gt;
‍&lt;/p&gt;
&lt;p&gt;Copy your token (anything after the = sign) and paste it into the Flood Agent terminal window.&lt;/p&gt;
&lt;h3 id="choosing-load-testing-tools"&gt;Choosing load testing tools&lt;/h3&gt;
&lt;p&gt;Next, you can choose which tools you&amp;rsquo;d like to set up and which version of those tools Flood Agent uses to run your tests.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200407-06.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Starting with JMeter, Flood Agent asks you which of the tools you&amp;rsquo;d like to configure. You can choose to set up your tools at the beginning so that you don&amp;rsquo;t have to set them up again, or you can go back to this configuration wizard at any point to add another tool. Either way, you need to provide the tools that you select in one of two ways: via a Docker image supplied by Flood, or via a &lt;a href="https://guides.flood.io/infrastructure/standalone-infrastructure/getting-started-local-jmeter-and-gatling"&gt;local installation&lt;/a&gt; that you already have.&lt;/p&gt;
&lt;p&gt;For this example, I chose to configure JMeter and to use a local installation. Local installations give you the most flexibility because you can use the version of the tool that you prefer, along with any special plugins. However, if you already have Docker installed or don&amp;rsquo;t want to manually install JMeter separately, you can also use our Docker image for JMeter.&lt;/p&gt;
&lt;p&gt;Selecting a local installation prompts you to provide the location of that directory:&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200407-07.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;For JMeter, this looks something like &lt;code&gt;/Users/nvanderhoeven/jmeter/apache-jmeter-5.1.1.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After you enter this file path, Flood Agent checks to make sure the directory is valid. You can also set the configuration for other tools in this dialog.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200407-08.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;After you finish the configuration, the wizard writes to a config.yaml file in your Flood Agent directory. You can also edit this configuration file directly in the future if you prefer.&lt;/p&gt;
&lt;h2 id="verifying-flood-agent-configuration"&gt;Verifying Flood Agent configuration&lt;/h2&gt;
&lt;p&gt;To run a final check on whether Flood Agent has been installed correctly, type &lt;code&gt;./flood-agent check&lt;/code&gt;. You should see a summary of the configuration options you selected as well as some connectivity and validity checks, like this:&lt;/p&gt;
&lt;p&gt;‍
&lt;img src="https://nicolevanderhoeven.com/assets/20200407-09.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;These green check marks mean you&amp;rsquo;re good to go! If you&amp;rsquo;re running Flood Agent from within a corporate network, though, you may see some errors here.&lt;/p&gt;
&lt;h2 id="troubleshooting-network-connectivity-within-a-corporate-network"&gt;Troubleshooting network connectivity within a corporate network&lt;/h2&gt;
&lt;p&gt;If your company already has a web proxy, check out &lt;a href="https://guides.flood.io/infrastructure/standalone-infrastructure/deployment/integrating-with-a-web-proxy"&gt;our guide on running floods through this proxy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please ensure that the load generator that you&amp;rsquo;ve installed Flood Agent on has access to the following endpoints:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;https://drain.flood.io&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;https://beacon.flood.io&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;https://vault.flood.io&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;https://flood-archives.s3-accelerate.amazonaws.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;https://logs.us-east-1.amazonaws.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;https://sns&amp;gt;.*.amazonaws.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;https://sqs&amp;gt;.*.amazonaws.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may need to request these from your IT department.&lt;/p&gt;
&lt;p&gt;If you get stuck, please contact us at &lt;a href="mailto:support@flood.io"&gt;support@flood.io&lt;/a&gt;. We&amp;rsquo;d love to go over your specific situation and get you up and running.&lt;/p&gt;
&lt;p&gt;Once Flood Agent returns a successful check, you can start up your own grid!&lt;/p&gt;
&lt;h2 id="turning-your-machine-into-a-flood-grid"&gt;Turning your machine into a Flood grid&lt;/h2&gt;
&lt;p&gt;A grid, in Flood terminology, is a group of machines to be used for load generation. To make your machine a grid, run this command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./flood-agent --grid askaflooder
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;where &lt;code&gt;askaflooder&lt;/code&gt; is the name of your grid.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200407-10.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Once you see those [agent] ready and awaiting next job messages, you&amp;rsquo;re ready to run your test!&lt;/p&gt;
&lt;h2 id="running-a-load-test-on-your-own-infrastructure"&gt;Running a load test on your own infrastructure&lt;/h2&gt;
&lt;p&gt;Log into your account on Flood, if you aren&amp;rsquo;t already, and &lt;a href="https://guides.flood.io/overview-of-flood/scripting-your-load-test#creating-a-stream"&gt;create a new stream&lt;/a&gt; like you normally would, uploading your load testing script and data files. When &lt;a href="https://guides.flood.io/overview-of-flood/launching-your-test"&gt;launching your test&lt;/a&gt;, though, select the option for Hosted infrastructure and then choose the grid with Flood Agent running on it.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200407-11.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Go ahead and launch your test. Congratulations, you&amp;rsquo;re now set up to run a load test on your on-premise load generators!&lt;/p&gt;
&lt;h2 id="giving-it-a-go"&gt;Giving it a go&lt;/h2&gt;
&lt;p&gt;Flood Agent is a free download, so you&amp;rsquo;ll only need to pay when you run load tests on Flood. However, we also have a free tier— perfect for a proof-of-concept while you&amp;rsquo;re working from home.&lt;/p&gt;
&lt;p&gt;If you have any questions about Flood Agent, check out &lt;a href="https://guides.flood.io/infrastructure/standalone-infrastructure/flood-agent"&gt;our documentation&lt;/a&gt; or &lt;a href="mailto:support@flood.io"&gt;email us&lt;/a&gt;. We&amp;rsquo;d be happy to get you started!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/flood-io/flood-agent/releases"&gt;Download Flood Agent for free here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200407-12.gif" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;</description></item><item><title>Ask a Flooder 09: What are the best tools for load testing?</title><link>https://nicolevanderhoeven.com/blog/20200331-aaf09/</link><pubDate>Tue, 31 Mar 2020 20:35:36 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200331-aaf09/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-09-what-are-the-best-tools-for-load-testing-video"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-09-what-are-the-best-tools-for-load-testing-video"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One of our most commonly-asked questions is: &amp;ldquo;What are the best tools for load testing?&amp;rdquo; The answer will depend on a few things: your reason for load testing, the application you want to test, what kind of load testing you want to carry out, your team&amp;rsquo;s experience with scripting, and how many virtual users you want to run.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: In this video, I say that traditional load testing tools can&amp;rsquo;t simulate requests that are triggered by interactions on the client-side. This isn&amp;rsquo;t technically true: if you know what the requests are, you can build those requests manually on a protocol-level tool (like JMeter or Gatling). However, this takes more work compared to browser-level tools that just do this natively.&lt;/em&gt;
‍
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/I5Wo6x2OoZU?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Transcript:&lt;/p&gt;
&lt;p&gt;Hi everyone, Nicole back again with another Ask a Flooder, and this time I&amp;rsquo;m tackling one of the questions we get asked A LOT, which is:&lt;/p&gt;
&lt;h2 id="what-are-the-best-tools-for-load-testing"&gt;What are the best tools for load testing?&lt;/h2&gt;
&lt;h3 id="why-open-source-load-testing-tools-are-awesome"&gt;Why open-source load testing tools are awesome&lt;/h3&gt;
&lt;p&gt;Now, I want to preface this by saying that we really love open-source tools here at Flood, because 1) they&amp;rsquo;re really pretty full-fledged these days. You don&amp;rsquo;t have to make any compromises on features, and, in fact, some of these open-source tools have more features than some newer load testing tools just because they&amp;rsquo;ve been out longer. And secondly, they have large communities. Because they&amp;rsquo;re open-source, lots of people are using it, and lots of people are developing for it. So there are lots of plugins normally, and they support any kind of protocol that you can think of. And lastly, cost. It&amp;rsquo;s hard not to mention cost because it can be a really important factor depending on the budget of your project. And if you&amp;rsquo;re starting out as a load tester, I think that it might even be the best way to start with load testing, because you can take that knowledge with you and apply it to other projects and other companies regardless of the budget that they have.&lt;/p&gt;
&lt;h3 id="questions-to-ask-yourself-in-choosing-a-load-testing-tool"&gt;Questions to ask yourself in choosing a load testing tool&lt;/h3&gt;
&lt;h4 id="why-are-you-doing-load-testing"&gt;Why are you doing load testing?&lt;/h4&gt;
&lt;p&gt;I always advocate starting with WHY. Why are you actually load testing? Is there a particular production defect that you noticed that you&amp;rsquo;re trying to solve? Is there already a problem, or are you just preparing to launch? These are things that are going to inform the type of tool that you use because you might want to make sure that that tool supports the protocols that you need.&lt;/p&gt;
&lt;h4 id="what-are-you-load-testing"&gt;What are you load testing?&lt;/h4&gt;
&lt;p&gt;Secondly, what type of app are you testing? Because if you have a single-page app, where your end-user is interacting with the page and client-side scripts are running to make requests to servers, then that&amp;rsquo;s something that most traditional load testing tools aren&amp;rsquo;t going to be able to do.&lt;/p&gt;
&lt;p&gt;There are two general types of load testing tools: there&amp;rsquo;s browser-based, and there&amp;rsquo;s protocol-based. I&amp;rsquo;ll leave a link in the description talking about the differences between the two, but in general, if you want to go for browser-level testing, I would suggest Selenium and Element. Now, they&amp;rsquo;re both very good options. Selenium is probably the more robust in terms of automation of the two, but Element is better one in terms of efficiency because it was built with performance in mind. So Selenium&amp;rsquo;s automation-first and Element is really performance-first.&lt;/p&gt;
&lt;p&gt;Now, for protocol-level tools, you can&amp;rsquo;t go wrong with either JMeter or Gatling.&lt;/p&gt;
&lt;h4 id="what-experience-does-your-team-have-with-scripting"&gt;What experience does your team have with scripting?&lt;/h4&gt;
&lt;p&gt;Thirdly, what&amp;rsquo;s the level of technical expertise that your team has? I think it&amp;rsquo;s important to be realistic about what your team can do, given the amount of time that they have. So if you have a team that has never done load testing and has never done any scripting before, then I would suggest using our Test Builder or something similar. On Flood, we have a Test Builder that is really just an easy interface that actually runs JMeter under the hood, but it just kind of simplifies JMeter and boils it down to what are the URLs that you want to test. That is a really easy way to get started, and it&amp;rsquo;s also a great [way to run a] proof of concept. If you have some automation testers on your team that are trying to do the load testing, well, maybe you should stick to tools that they already use. So it might be easier for them to get up and running with either Selenium or Element rather than learning a new tool. And if you have developers on your team, you might consider Ruby JMeter rather than the vanilla JMeter because Ruby JMeter is a lot easier to deal with&amp;mdash; because you don&amp;rsquo;t have to deal with the XML files of a JMeter file (a normal JMX script). You can just plan out your load tests in Ruby. Or, Gatling is also a really good option because it allows you to write scripts in Scala. Scala is really really powerful, and you can create an entire framework with Gatling, which is also something to keep in mind.&lt;/p&gt;
&lt;h4 id="how-many-virtual-users-do-you-want-to-run-in-your-load-tests"&gt;How many virtual users do you want to run in your load tests?&lt;/h4&gt;
&lt;p&gt;And lastly, how many users do you want to simulate? Now protocol-level tools are going to really shine here because Element and Selenium both require a higher usage of resources on your load generators just because of the complexities involved in starting a browser instance, whereas protocol-level tools are just super efficient at sending these requests. So I would suggest that if you&amp;rsquo;re talking about thousands of users, I would use the protocol-level tools unless there&amp;rsquo;s a really good reason why that&amp;rsquo;s not going to give you the results that you&amp;rsquo;re looking for. Between JMeter and Gatling&amp;ndash; well, they&amp;rsquo;re both excellent tools, but Gatling has been shown to particularly be good for really high levels of usage. But JMeter is also a good alternative, and you can run thousands of users of JMeter without any problem.&lt;/p&gt;
&lt;h2 id="just-get-started"&gt;Just get started!&lt;/h2&gt;
&lt;p&gt;As always, I&amp;rsquo;d suggest just getting started. Pick whatever tool sounds like it might be the best for you, do a proof of concept on that one, and you can always try the other ones as well. On Flood, we don&amp;rsquo;t restrict you to just using one tool, so you can use one; you can use all four; you can run them at the same time if you want. It all depends on what you need for your testing.&lt;/p&gt;
&lt;p&gt;Till next time, happy flooding!&lt;/p&gt;</description></item><item><title>Ask a Flooder 08: Why is concurrent users an ambiguous measure of throughput?</title><link>https://nicolevanderhoeven.com/blog/20200326-aaf08/</link><pubDate>Thu, 26 Mar 2020 20:41:22 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200326-aaf08/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-08-why-is-concurrent-users-an-ambiguous-measure-of-throughput-video"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-08-why-is-concurrent-users-an-ambiguous-measure-of-throughput-video"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What is the meaning of concurrent users in performance testing? You might have seen concurrent users in JMeter or other tools, but what does it mean and how can you use the term properly?&lt;/p&gt;
&lt;p&gt;&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/uJE2hm50TIw?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
‍&lt;/p&gt;
&lt;p&gt;Transcript:&lt;/p&gt;
&lt;p&gt;Hi everyone! Nicole here again for another Ask a Flooder, except this time it&amp;rsquo;s not really a question&amp;ndash; it&amp;rsquo;s just something I want to talk about.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200326-01.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: The &lt;a href="https://twitter.com/flood_io/status/1224284942932332544"&gt;Flood Twitter account&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m going to put the tweet up here, but last month I actually tweeted about something that somebody else posted. Netflix actually reported some of their viewership figures, and one thing that they reported was that 76 million households watched their new show, The Witcher, which is a large amount of people. So I posted something about why concurrent users is actually an ambiguous way to measure throughput, and I want to expound a little bit more on that.&lt;/p&gt;
&lt;h2 id="concurrent-users-in-performance-testing---limitations"&gt;&amp;ldquo;Concurrent users&amp;rdquo; in performance testing - limitations&lt;/h2&gt;
&lt;p&gt;In load testing, the number of concurrent users is a pretty standard measure of throughput, and generally, it means the number of users that are accessing an application at any one point in time. And even on Flood, we use it because it&amp;rsquo;s a really good rule of thumb. When we want to get a feel for how big a test is, load testers generally ask, &amp;ldquo;How many users?&amp;rdquo; However, we should be aware, as load testers, that that doesn&amp;rsquo;t always give us the full picture.&lt;/p&gt;
&lt;h3 id="what-type-of-user-is-it"&gt;What type of user is it?&lt;/h3&gt;
&lt;p&gt;For instance, the type of user that it is can really change the traffic. Sometimes mobile browsers will optimize requests so that they send fewer because they realize that mobile networks are slower.&lt;/p&gt;
&lt;h3 id="what-is-the-user-doing"&gt;What is the user doing?&lt;/h3&gt;
&lt;p&gt;Secondly, what is the user doing? Are they browsing on different tabs all the time? Or are they just refreshing the content on the same tab? Because if they&amp;rsquo;re just using the same page and fetching it over and over again, then caching could come into play, so that would create a different load on the system.&lt;/p&gt;
&lt;h3 id="how-long-is-the-user-doing-it"&gt;How long is the user doing it?&lt;/h3&gt;
&lt;p&gt;And thirdly, how long is the user doing it? Are we talking about a sustained load for an hour, or is it a shorter time period, like a spike test of five minutes where all the users just ram a system? There are things like the number of requests per second that you need to take into account, which is affected by the think time and the delays that you set in your script. Perhaps your script has a user that just sends a request, then waits for a minute, and then sends another request. That&amp;rsquo;s very different from a user just sending one request after another as fast as possible.&lt;/p&gt;
&lt;h3 id="when-does-the-user-do-it"&gt;When does the user do it?&lt;/h3&gt;
&lt;p&gt;Lastly, it&amp;rsquo;s also important to take into account the seasonality of the request. When does the user do it? If you have a timesheeting application, for instance, then it might only get traffic on Fridays and a little bit on Monday, so you need to specify, when you report these numbers, whether it&amp;rsquo;s for the peak time period on Friday - you know, around 3 pm or something - or you&amp;rsquo;re reporting on figures from a Wednesday when there really wouldn&amp;rsquo;t be that many people accessing it.&lt;/p&gt;
&lt;p&gt;In the case of Netflix, what they counted as a view was only a two-minute watch time. So if you&amp;rsquo;re one of those people who went into The Witcher and didn&amp;rsquo;t watch past two minutes, that still counted as a view. So the 76 million users is still not completely accurate, because not all of those people would have watched the entire thing.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Concurrent users is a good way in order to get a quick feel of the throughput that a test outputs, but it really needs to be examined in context along with things like requests per second and the network throughput (kbps) in order to get a really good understanding of how much load a test is generating.&lt;/p&gt;
&lt;p&gt;Till next time, happy flooding!&lt;/p&gt;</description></item><item><title>Ask a Flooder 06: How to record traffic from a mobile app with JMeter</title><link>https://nicolevanderhoeven.com/blog/20200319-aaf06/</link><pubDate>Thu, 19 Mar 2020 20:50:44 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200319-aaf06/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/ask-a-flooder-06-how-to-record-traffic-from-a-mobile-app-with-jmeter-video"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/ask-a-flooder-06-how-to-record-traffic-from-a-mobile-app-with-jmeter-video"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;How do you record the traffic from your native mobile app using Apache JMeter? In this video, I explain step-by-step how to begin load testing a native mobile app.&lt;/p&gt;
&lt;p&gt;‍&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/VVUyYF1VPlo?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;You might want to be doing this because you&amp;rsquo;re load testing a mobile app but either you don&amp;rsquo;t have access to your developers right now so they can&amp;rsquo;t help you with how the requests are made or what the requests are, or maybe you just want to verify what you&amp;rsquo;ve already been told. Either way, I&amp;rsquo;m going to show you how using my Android device (it&amp;rsquo;s a Samsung Galaxy Note 10+) and a Macbook Pro 13&amp;quot;. However, the same principles should apply to other mobile devices and laptops.&lt;/p&gt;
&lt;h2 id="step-1-download-and-install-the-mobile-app"&gt;Step 1: Download and install the mobile app&lt;/h2&gt;
&lt;p&gt;The first thing you&amp;rsquo;ll want to do is download and install the app. Now, if the app has already been released, then that&amp;rsquo;s no problem. You just go to the Google Play Store and download it. However, if it hasn&amp;rsquo;t been released yet and you&amp;rsquo;ve been given an APK file for the app, then you&amp;rsquo;ll have to tweak your settings a little bit in order to get your phone to allow you to download it.&lt;/p&gt;
&lt;p&gt;And I&amp;rsquo;m going to do this right with you. So go into Settings: pull down the little notification screen and hit the little gear icon, and you&amp;rsquo;ll see a bunch of settings there. Scroll down all the way to the bottom until you see About phone. Click that and then click Software Information. You&amp;rsquo;ll see the Build number halfway through the screen here&amp;ndash; and I know this is weird, but click on it seven times. You have to draw your pattern if you have one set up, but you&amp;rsquo;ll see here that it says Developer Mode has been enabled. That&amp;rsquo;s all you need to do; from now on you can install any APK, but be careful, because if you don&amp;rsquo;t trust the source, you know, they could do some damage to your phone.&lt;/p&gt;
&lt;h2 id="step-2-set-up-the-https-test-script-recorder-on-jmeter"&gt;Step 2: Set up the HTTP(S) Test Script Recorder on JMeter&lt;/h2&gt;
&lt;p&gt;The second step is setting up an HTTP recorder on JMeter. So let&amp;rsquo;s head over to our laptop. Make sure you&amp;rsquo;re connected to a wifi network. Now, this is a completely new JMeter test plan here. So just right-click on Test Plan and then hover over Add, and then you&amp;rsquo;re going to go down to Non-Test Elements. Select the HTTP(S) Test Script Recorder. You&amp;rsquo;ll see here that the port it uses is 8888. We&amp;rsquo;ll need that for later. Now right-click on the Test Script Recorder, hit Add, and then Listener. We&amp;rsquo;ll add a View Results Tree to be able to capture the results that we need later. Now we&amp;rsquo;ll click on the Test Plan - right-click - and then we&amp;rsquo;re going to Add a Thread Group. Now let&amp;rsquo;s right-click on the Thread Group and go to Add &amp;gt; Logic Controller &amp;gt; Recording Controller. This is where the requests that we&amp;rsquo;re going to send are going to be recorded, and we&amp;rsquo;ll see both the requests and the responses in View Results Tree.&lt;/p&gt;
&lt;h2 id="step-3-set-up-your-mobile-to-use-the-https-test-script-recorder-as-a-proxy"&gt;Step 3: Set up your mobile to use the HTTP(S) Test Script Recorder as a proxy&lt;/h2&gt;
&lt;p&gt;Now, the third step is to set up your mobile to use a proxy. So, back on the mobile, go back to Settings and hit Connections. And then click on Wifi. Now verify that the wifi network that you&amp;rsquo;re connected to on your mobile is the same one as the wifi network that you&amp;rsquo;re connected to on your laptop. If it&amp;rsquo;s the same, click the gear icon. And then hit Advanced all the way at the bottom. Now the Proxy settings are on the second option here so hit the down menu here and then hit Manual. Now, for the Proxy host name we&amp;rsquo;ll have to go back to the computer to see what our local IP address is.&lt;/p&gt;
&lt;p&gt;So, in order to do that, you just have to go to System Preferences. I&amp;rsquo;m doing it from the Apple menu here and from System Preferences, click on Network. And once you&amp;rsquo;ve selected the wifi network, hit Advanced, and then TCP/IP. So what you&amp;rsquo;re looking for will be this IPv4 Address. Now you&amp;rsquo;re going to type that into the Host name field on your mobile. So let me just type that in.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll note that this is actually a local IP address. It&amp;rsquo;s not your public IP address. Now for our proxy port, we&amp;rsquo;ll go and look at the laptop to see the Test Script Recorder Settings and it was 8888, so that&amp;rsquo;s what we&amp;rsquo;re going to put in here. All right. And then click on Save.&lt;/p&gt;
&lt;h2 id="step-4-start-recording"&gt;Step 4: Start recording&lt;/h2&gt;
&lt;p&gt;Step 4 is recording the traffic. Now, go back to your laptop on JMeter and click on the Test Script Recorder. Now we&amp;rsquo;re going to click on Start. It&amp;rsquo;ll come up with this popup box here and you can just click OK since we already trust the source. You&amp;rsquo;ll see this little popup here to confirm that we are recording traffic. Now let&amp;rsquo;s see if we&amp;rsquo;ve actually configured it correctly.&lt;/p&gt;
&lt;p&gt;On our mobiles, let&amp;rsquo;s go back and open the app that you want to use. So I&amp;rsquo;m going to be using the IowaReporterApp. I just did an article on this on The New Stack (link in the description below) about how I would load test the Iowa Caucus Reporter App. So that&amp;rsquo;s what I&amp;rsquo;m going to be using for this tutorial as well. So I&amp;rsquo;m going to open that (&amp;ldquo;getting ready to caucus&amp;rdquo;) and I don&amp;rsquo;t have a valid precinct ID so I&amp;rsquo;m just going to put flood.io and I&amp;rsquo;m going to click Login. So it&amp;rsquo;s telling me that the connection isn&amp;rsquo;t private. I don&amp;rsquo;t actually expect this to work, because the app has been taken down and I don&amp;rsquo;t have valid login credentials, but I&amp;rsquo;m still just going to go forward. So, Proceed. You might have to do this as well if your certificates haven&amp;rsquo;t been set up correctly.&lt;/p&gt;
&lt;p&gt;Okay, so now we&amp;rsquo;ve gotten this error here and it says, &amp;ldquo;Oops, something went wrong&amp;rdquo;. So it&amp;rsquo;s handy to know what it looks like on your mobile so that you can go into JMeter and see if that&amp;rsquo;s what you see as well.&lt;/p&gt;
&lt;p&gt;So on JMeter let&amp;rsquo;s go to the View Results Tree and it looks like Google Play tried to do a few things beforehand but what we&amp;rsquo;re really looking for is this IDP caucus thing. So, these are the requests that my phone made and these are the responses. So this is where we got the certificate error because it says &amp;ldquo;certificate unknown&amp;rdquo;. Now this is the actual request we sent for login. You can see the request here - the full request - and also the full response. Now, just to check we&amp;rsquo;ll make sure we got the same response in the body here on JMeter that we saw on our mobile phones.&lt;/p&gt;
&lt;p&gt;And here it is; it says &amp;ldquo;Oops!, something went wrong&amp;rdquo;. You can of course just search for this. So we&amp;rsquo;ll stop the recording now because we&amp;rsquo;ve confirmed that we&amp;rsquo;ve gotten what we wanted. So in the recording controller, we can see the requests that were recorded from our mobile app and the first one looks like it was one to Google so that&amp;rsquo;s not one we want. If we were doing this again, we could actually go into the Test Script Recorder, go into this Requests Filtering tab, and we could just filter out or exclude the domains that we don&amp;rsquo;t want to test, like Google. But this looks like the request that we were actually looking for. You&amp;rsquo;ll see the full request here with all the parameters and the values.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next?&lt;/h2&gt;
&lt;p&gt;And that&amp;rsquo;s how you record traffic from a native mobile app on JMeter. From here, you can then play around with the request. Like we saw, there were a lot of dynamic values that were recorded in the request. We would probably want to correlate those and make sure that we&amp;rsquo;re doing those correctly. But this is a really great starting point for testing a mobile app.&lt;/p&gt;
&lt;p&gt;Till the next Ask a Flooder, happy Flooding!&lt;/p&gt;</description></item><item><title>FOMO and performance testing: Why Robinhood went down</title><link>https://nicolevanderhoeven.com/blog/20200309-fomo-and-performance-testing/</link><pubDate>Mon, 09 Mar 2020 20:29:40 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200309-fomo-and-performance-testing/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/fomo-and-performance-testing-why-robinhood-went-down"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/fomo-and-performance-testing-why-robinhood-went-down"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Angry customers flocked to the internet when the Robinhood investment brokerage app went down last Monday and part of Tuesday, leaving them unable to trade stocks on a day where Wall Street reported historic gains. Could this have been prevented?&lt;/p&gt;
&lt;p&gt;The answer is yes. Applications should always be assessed for risk and tested accordingly. Risk-based load testing helps unearth problems that would otherwise only surface in production. Testing applications end-to-end as a system instead of just as a collection of isolated components allows us to build software that is more resilient. The problem is that this type of testing can be difficult to carry out. In this article, I&amp;rsquo;ll go through the entire process.&lt;/p&gt;
&lt;h2 id="what-is-robinhood"&gt;What is Robinhood?&lt;/h2&gt;
&lt;p&gt;Robinhood is an app I&amp;rsquo;ve personally had my eye on since it opened a waiting list about five years ago for Australians wanting to see the American company come to our shores. It &lt;a href="https://australianfintech.com.au/local-start-stake-beats-us-giant-robinhood-bring-australians-free-share-trading/"&gt;doesn&amp;rsquo;t look like it&amp;rsquo;s happening&lt;/a&gt; now, but if that waiting list still exists, I&amp;rsquo;m on it.&lt;/p&gt;
&lt;p&gt;Robinhood is a brokerage app that takes inspiration from the legend it was named after to &amp;ldquo;give to the poor,&amp;rdquo; bring zero-commission trading to its customers. Instead of earning from commissions, Robinhood makes its money from &lt;a href="https://www.investopedia.com/articles/active-trading/020515/how-robinhood-makes-money.asp"&gt;a variety of other methods&lt;/a&gt;, including premium platform fees, interest on customers&amp;rsquo; uninvested capital, and payments received in exchange for coursing customers&amp;rsquo; orders through third-party market-makers. It&amp;rsquo;s an enticing premise, and one that the company has used in conjunction with mobile apps to appeal to millennials and technophiles.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: From &lt;a href="https://robinhood.com/us/en/"&gt;Robinhood&amp;rsquo;s homepage&lt;/a&gt;&lt;/em&gt;
‍&lt;/p&gt;
&lt;h2 id="what-caused-the-robinhood-app-to-fail"&gt;What caused the Robinhood app to fail?&lt;/h2&gt;
&lt;p&gt;Unfortunately, its wild success with the tech-savvy crowd also meant that when it went down last Monday morning in the US, its customers took to the internet in droves to draw attention to the failure. In a &lt;a href="https://blog.robinhood.com/news/2020/3/3/an-update-from-robinhoods-founders"&gt;blog post from Robinhood&lt;/a&gt;, the company&amp;rsquo;s co-founders, Baiju Bhatt and Vladimir Tenev, admitted that the outage was due to &amp;ldquo;stress on [their] infrastructure— which struggled with unprecedented load.&amp;rdquo;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The outages you have experienced over the last two days are not acceptable and we want to share an update on the current situation. Our team has spent the last two days evaluating and addressing this issue. We worked as quickly as possible to restore service, but it took us a while. Too long. - &lt;a href="https://blog.robinhood.com/news/2020/3/3/an-update-from-robinhoods-founders"&gt;Baiju Bhatt and Bladimir Tenev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Bhatt and Tenev went on to explain that the load on their servers had caused a &amp;ldquo;thundering herd&amp;rdquo; effect. This effect describes a situation in which a backend server receives a large number of concurrent requests and, instead of different threads processing these requests simultaneously, all threads attempt to process the same request.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve ever played four-player Overcooked, it&amp;rsquo;s like having everyone sprint to a fire extinguisher to try to put out a fire from overcooked pasta, but instead, the constant button mashing means you just pass the extinguisher around and yell at each other while the fire rages on. Before you know it, the entire kitchen is on fire.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-02.jpeg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: From &lt;a href="https://twitter.com/csac0425/status/963053160259510277"&gt;Twitter user @csac0425&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So the technical cause of the outage was an infrastructure problem.&lt;/p&gt;
&lt;p&gt;But I think the REAL cause was FOMO.&lt;/p&gt;
&lt;h2 id="what-is-fomo"&gt;What is FOMO?&lt;/h2&gt;
&lt;p&gt;FOMO is the Fear Of Missing Out, and it&amp;rsquo;s a phenomenon that&amp;rsquo;s been particularly exacerbated by social media. Instead of only hearing news from people you interact with in the physical world, it just takes a few seconds to open up Twitter on your smartphone and see what thousands of people are talking about. Twitter will even helpfully tell you what&amp;rsquo;s &amp;ldquo;trending&amp;rdquo; in your country, and other social networks also employ algorithms to determine what messages to show you.&lt;/p&gt;
&lt;h2 id="how-does-fomo-affect-application-performance"&gt;How does FOMO affect application performance?&lt;/h2&gt;
&lt;p&gt;We can see FOMO in how people have reacted to the coronavirus COVID-19. Despite pleas to leave masks for medical professionals at real risk, people have bought out the stock for these masks despite living in countries with little to no sign of the coronavirus. In Sydney, Australia, residents are stockpiling toilet paper (of all things) in large quantities, clearing out shelves from supermarkets, and &lt;a href="https://www.bbc.com/news/world-australia-51731422"&gt;getting into knife fights over toilet paper&lt;/a&gt; due to their panic at the thought of doing without.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-03.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This photo of an empty toilet paper aisle was taken by our Principal Engineer, &lt;a href="https://github.com/lachie"&gt;Lachie Cox&lt;/a&gt;, in a supermarket in Sydney last week.&lt;/em&gt;
‍
FOMO of this magnitude ultimately also manifests itself in the stock market. The Dow Jones Industrial Average, which represents the performance of the top 30 largest companies in the US, dropped by 3,500 points last week amidst fears that the coronavirus would stall production in countries most affected by COVID-19, leading to lower returns across the board.&lt;/p&gt;
&lt;p&gt;Then, an abrupt about-face. The Dow went from recording its &lt;a href="https://nypost.com/2020/03/09/dow-loses-over-2000-points-worst-single-day-drop-ever-as-oil-prices-crash/"&gt;biggest &lt;em&gt;ever&lt;/em&gt; loss in a single day on Thursday&lt;/a&gt; to posting a record gain of 1,200 points on Monday.&lt;/p&gt;
&lt;p&gt;Guess who gets to adjust to all this volatility.&lt;/p&gt;
&lt;p&gt;Robinhood&amp;rsquo;s founders reported that the &amp;ldquo;highly volatile and historic market conditions; record volume; and record sign-ups&amp;rdquo; contributed to the infrastructure issues they faced. We can only imagine just how many users they saw on their systems during this period.&lt;/p&gt;
&lt;p&gt;FOMO creates mass hysteria and panic, both of which cause customers to behave irrationally and likely very differently than previously recorded. So how do you create a workload model that accounts for this volatility? How do you tailor your load tests for FOMO? I&amp;rsquo;ll run you through the process, using Robinhood as an example.&lt;/p&gt;
&lt;h2 id="does-your-application-require-fomo-load-testing"&gt;Does your application require FOMO load testing?&lt;/h2&gt;
&lt;p&gt;All testing should be risk-based. Start with why: why should you test an application component? Why does your application require this kind of testing or that one?&lt;/p&gt;
&lt;p&gt;In Robinhood&amp;rsquo;s case, there were a few indicators that pointed to the necessity of FOMO testing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A customer base made up of a younger, tech-savvy generation&lt;/strong&gt;. ‍This demographic is very likely to take to social media with complaints, making negative publicity a real risk.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complexity&lt;/strong&gt;. ‍Robinhood&amp;rsquo;s systems must receive customers&amp;rsquo; orders, forward and place those orders to market makers, and display real-time market data. Robinhood customers can also purchase options on stocks as well as cryptocurrency, further increasing the complexity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Financial component&lt;/strong&gt;. ‍Anything that involves taking people&amp;rsquo;s money should have a high priority for any testing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;When it comes to your money, we know how important it is for you to have answers. - &lt;a href="https://blog.robinhood.com/news/2020/3/3/an-update-from-robinhoods-founders"&gt;Baiju Bhatt and Bladimir Tenev&lt;/a&gt;, co-founders of Robinhood&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Record-breaking growth&lt;/strong&gt;. ‍Robinhood had 1 million users in 2016, 6 million in 2018, and 10 million last December, &lt;a href="https://www.cnbc.com/2019/12/04/start-up-robinhood-tops-10-million-accounts-even-as-industry-follows-in-free-trading-footsteps.html"&gt;according to CNBC&lt;/a&gt;. Exponential growth brings some major growing pains.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Industry history of expensive failures&lt;/strong&gt;. ‍&lt;a href="https://qz.com/1812086/robinhood-brokerage-app-suffers-second-day-of-outages/"&gt;Quartz points out&lt;/a&gt; that software or infrastructure failures have cost the financial industry millions in recent years.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Recent legal action&lt;/strong&gt;. The regulatory body FINRA &lt;a href="https://www.cnn.com/2019/12/19/investing/robinhood-trading-app-fine-finra/index.html"&gt;fined Robinhood&lt;/a&gt; US$1.25 million just last December for placing orders for customers without looking for the lowest price. While this isn&amp;rsquo;t a huge fine for a company that was &lt;a href="https://www.bloomberg.com/news/articles/2019-07-22/robinhood-is-worth-7-6-billion-after-new-funding-round"&gt;valued at US$7.6 billion last July&lt;/a&gt;, any legal action should make a company tread more carefully.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are solid reasons to warrant load testing Robinhood&amp;rsquo;s systems beyond just &amp;ldquo;expected&amp;rdquo; load.&lt;/p&gt;
&lt;h2 id="planning-for-fomo"&gt;Planning for FOMO&lt;/h2&gt;
&lt;p&gt;Once you&amp;rsquo;ve determined that your application is susceptible to FOMO, the next thing to do is plan how to structure your load tests to include its effect in your test runs.&lt;/p&gt;
&lt;h3 id="what-type-of-app-is-it"&gt;What type of app is it?&lt;/h3&gt;
&lt;p&gt;Robinhood consists of three apps: two for mobile (iOS and Android) and one for the web. It is currently only available to US residents, which means that getting mobile apps downloaded is problematic as it would entail changing countries in the app stores to get access. Since I live in the Netherlands and I don&amp;rsquo;t want to do that, I&amp;rsquo;ll focus on the web app specifically when scripting the load test, and I&amp;rsquo;ll assume that the same underlying application servers service both web and mobile apps.&lt;/p&gt;
&lt;h3 id="workload-modeling"&gt;Workload modeling&lt;/h3&gt;
&lt;h4 id="business-processes-to-test"&gt;Business processes to test&lt;/h4&gt;
&lt;p&gt;Given that the app went down on a day where markets were &lt;em&gt;up&lt;/em&gt;, we can assume that most of the people on Robinhood&amp;rsquo;s apps were doing one of two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Signing up&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Buying shares&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We should ideally test both, but let&amp;rsquo;s focus on signups here because it&amp;rsquo;s easier to test without access to the actual app (which requires a US social security number).&lt;/p&gt;
&lt;h4 id="number-of-virtual-users-at-peak-load"&gt;Number of virtual users at peak load&lt;/h4&gt;
&lt;p&gt;Let&amp;rsquo;s try to come up with a number of users that we can use as Robinhood&amp;rsquo;s peak hourly load. This task would be easier if Robinhood shared their analytics, but we can still make some educated guesses with what &lt;em&gt;has&lt;/em&gt; been made public.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-04.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: Image from &lt;a href="https://blog.apptopia.com/robinhood-now-has-more-mobile-monthly-active-users-than-the-top-legacy-providers-combined"&gt;Apptopia&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.apptopia.com/robinhood-now-has-more-mobile-monthly-active-users-than-the-top-legacy-providers-combined"&gt;According to Apptopia&lt;/a&gt;, Robinhood&amp;rsquo;s highest number of mobile installations was in 2018, when it saw 2 million downloads of its mobile app in a quarter. This period coincides with when Robinhood released cryptocurrency trading support, which looks like increased signups—in the third quarter of 2019, this figure was about 1.5 million. 2 million quarterly downloads translates to about 666,666 downloads per month. Let&amp;rsquo;s call that 700,000 and take this number of mobile downloads as a starting figure.&lt;/p&gt;
&lt;p&gt;The stock market is only open on the weekdays, so we can divide 700,000 signups a month by 20 days, and we get 35,000 signups a day. The markets are also only open for about 7 hours a day, but the load probably isn&amp;rsquo;t evenly spread out across the 7 hours— I would expect that people would have more time during lunch to sign up for Robinhood. So let&amp;rsquo;s say that the majority of those signups would happen within three hours (from 11 am to 2 pm, for example). By dividing 35,000 signups per day by 3 hours, we get about 11,667 users per hour. Let&amp;rsquo;s round that up to 12,000.&lt;/p&gt;
&lt;p&gt;How do we translate this to the number of virtual users we need to run? Let&amp;rsquo;s think about how long each user stays on the app. The signup process does require approval, so that&amp;rsquo;s a hard stop— new users won&amp;rsquo;t be able to sign up and then start trading immediately afterwards. &lt;a href="https://robinhood.com/us/en/support/articles/360001213123/open-my-account/"&gt;This approval could take up to a day&lt;/a&gt;, or up to seven days if documents are required.&lt;/p&gt;
&lt;p&gt;Looking at the application form itself, we can see it is relatively standard and requires only information that most people are likely to have handy (social security number and contact details). I timed how long it took me to go through the part of it I could access, and I estimated that 10 minutes would be sufficient time to go through all the tabs.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-05.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://signup.robinhood.com/?"&gt;Robinhood&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We want to spread out users evenly across the hour— that is, we don&amp;rsquo;t want 12,000 users to sign up and leave, all within 10 minutes. We want equal portions of those users to be signing up throughout the entire hour. Dividing 12,000 by 6 (the number of 10-minute intervals in an hour), we get 2,000. 2,000 is the number of threads we need active at any one point of time. Each of those threads signs up one user for 10 minutes, then signs up 5 more users until the hour is up.&lt;/p&gt;
&lt;p&gt;So we have our figure: we need 2,000 &lt;em&gt;virtual&lt;/em&gt; users per hour to simulate 12,000 signups in an hour.&lt;/p&gt;
&lt;h3 id="test-scenarios"&gt;Test scenarios&lt;/h3&gt;
&lt;p&gt;In addition to running the standard battery of load tests, testing for FOMO requires exploring the upper limits of the application. Assuming the peak load testing goes well, we can use the 2,000 virtual users per hour figure from our peak load calculations as a starting point for these more destructive tests. To this end, here are some test scenarios I&amp;rsquo;d consider running if I were testing the Robinhood app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Soak testing&lt;/strong&gt;. ‍Soak testing means running typically a little less than the peak load profile, but over a longer amount of time. In Robinhood&amp;rsquo;s case, perhaps we could run 1,000 virtual users for 8 hours. We would expect that the response times reported during this test would be the same; otherwise, there is likely a performance bottleneck, such as a memory leak.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stress testing&lt;/strong&gt;. ‍Stress testing means increasing the number of users on an application at regular increments until the application crashes. We can start with 2,000 virtual users per hour, and then add 500 users every 30 minutes to see how the application handles it. This test helps determine how much room there is for growth.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-06.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: Stepped load profile for stress testing, generated in JMeter&lt;/em&gt;
‍&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resilience testing&lt;/strong&gt;. ‍Resilience testing involves running the peak load test and then simulating an outage by turning off key components to see how the application behaves. If two servers share the load, for instance, we could turn one off and check to make sure that the user sessions on the terminated node are moved over onto the remaining node. This test shows whether the application recovers gracefully from an unforeseen event.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these tests help us prepare for the unexpected, and they might have identified the &amp;ldquo;thundering herd&amp;rdquo; problem that contributed to Robinhood&amp;rsquo;s outage.&lt;/p&gt;
&lt;h2 id="scripting-for-fomo"&gt;Scripting for FOMO&lt;/h2&gt;
&lt;h3 id="tool-selection"&gt;Tool selection&lt;/h3&gt;
&lt;p&gt;To test mobile as well as web apps, I would typically use a protocol-level load testing tool like JMeter to simulate the load. However, &lt;a href="https://thenewstack.io/how-id-load-test-the-iowa-caucus-app/"&gt;I&amp;rsquo;ve already done that&lt;/a&gt;. I also noticed that Robinhood makes use of some dynamic parameters, such as a csrf token:&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200309-07.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: Chrome Developer Tools on &lt;a href="https://signup.robinhood.com/?"&gt;Robinhood&lt;/a&gt;&lt;/em&gt;
‍
We can usually find the value of this token in the response of the previous page. It is a security feature to allow Robinhood to verify that the same users make all requests in the same session. Tokens can be scripted around in a tool like JMeter, but it can be time-consuming. So for this article, I decided to show another tool that makes this problem go away. I used &lt;a href="https://element.flood.io/"&gt;Flood Element&lt;/a&gt;, which is an open-source tool we created based on Puppeteer. One of its advantages is that it runs on the browser level. Instead of diving into HTTP requests and tokens, I just told Element which buttons to click.&lt;/p&gt;
&lt;p&gt;I wanted a script that would do the following things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to Robinhood&amp;rsquo;s home page.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on &amp;ldquo;Sign Up&amp;rdquo;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &amp;ldquo;Get Your Free Stock&amp;rdquo; button.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill out the signup form.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that I stopped short of actually submitting the form because I don&amp;rsquo;t want to create dummy accounts on Robinhood&amp;rsquo;s database— I just want to show how this flow can be scripted with Element.&lt;/p&gt;
&lt;p&gt;To help me identify the elements on the page that I wanted the script to interact with, I used Chrome&amp;rsquo;s built-in Developer Tools. Specifically, I used the Inspect Element button (in orange below) to click on a field and find where it was in the code.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200309-08.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;For instance, to find out how to identify the email field, I right-clicked on the code on the right, hovered over Copy, and then clicked Copy selector.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200309-09.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This gave me the value &lt;code&gt;#signup_email_field&lt;/code&gt;, which I then used in the script like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//Type Email address
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;signup_email_field&amp;#39;&lt;/span&gt;),&lt;span style="color:#e6db74"&gt;&amp;#39;nicole@flood.io&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the script that I ended up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;MouseButtons&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Device&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Driver&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;ENV&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;@flood/element&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;as&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;assert&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;assert&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loopCount&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clearCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;disableCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;actionDelay&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;stepDelay&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;screenshotOnFailure&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;userAgent&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;flood-element-test&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;01_Home&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Navigate to Robinhood homepage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;visit&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;https://robinhood.com&amp;gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Validate text
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;validation&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;visibleText&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;It’s Time to Do Money&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;wait&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;elementIsVisible&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;validation&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;02_Signup&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Click &amp;#34;Sign Up&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;signupBtn&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;findElement&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;xpath&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;//a[@href=&amp;#34;&amp;lt;https://signup.robinhood.com&amp;gt;&amp;#34;]&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;signupBtn&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;click&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Validate text
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;validation&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;visibleText&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Free Stock Waiting For You&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;wait&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;elementIsVisible&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;validation&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;03_GetFreeStock&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Click Get Your Free Stock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;freestockBtn&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;findElement&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;xpath&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;//a[@href=&amp;#34;&amp;lt;https://signup.robinhood.com/?&amp;#34;&amp;gt;]&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;freestockBtn&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;click&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Validate text
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;validation&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;visibleText&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Make Your Money Move&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;wait&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;elementIsVisible&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;validation&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;04_FillForm&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Type First name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;xpath&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;//input[@name=&amp;#34;first_name&amp;#34;]&amp;#39;&lt;/span&gt;),&lt;span style="color:#e6db74"&gt;&amp;#39;Nicole&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Type Last name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;xpath&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;//input[@name=&amp;#34;last_name&amp;#34;]&amp;#39;&lt;/span&gt;),&lt;span style="color:#e6db74"&gt;&amp;#39;van der Hoeven&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Type Email address
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;signup_email_field&amp;#39;&lt;/span&gt;),&lt;span style="color:#e6db74"&gt;&amp;#39;nicole@flood.io&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Type password
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;xpath&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;//input[@name=&amp;#34;password&amp;#34;]&amp;#39;&lt;/span&gt;),&lt;span style="color:#e6db74"&gt;&amp;#39;demo123456&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In every step, I also added a &lt;code&gt;takeScreenshot()&lt;/code&gt; , which is one of my very favorite features of Element. It&amp;rsquo;s so useful to be able to save these screenshots and use them to determine exactly what&amp;rsquo;s happening in the script. Here&amp;rsquo;s what it looks like running my Element script locally. The automated browser is on the left, and my terminal (iTerm) is on the right, running Element.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200309-10.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="running-a-fomo-test"&gt;Running a FOMO test&lt;/h2&gt;
&lt;p&gt;Now we&amp;rsquo;ve got a script that runs locally. How do we run it at scale? Additionally, Robinhood won&amp;rsquo;t allow users outside the US to sign up. How do we make sure that our virtual users are allowed to sign up?&lt;/p&gt;
&lt;p&gt;The answer is a load testing platform like &lt;a href="https://flood.io/"&gt;Flood&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With Flood, running our script is a matter of uploading the script and selecting some options.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-11.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The Flood test design interface&lt;/em&gt;
‍
Flood also lets us choose which regions to generate load from. Below I&amp;rsquo;ve got it set up to run for an hour from four different US cities, with each one starting 500 users. This configuration gives us our 2,000 virtual user figure.&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200309-12.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;If this peak load test goes well, we could then go on to run the other test scenarios that we described earlier. Each one puts pressure on application servers in different ways, identifying performance bottlenecks.&lt;/p&gt;
&lt;h2 id="results-and-reruns"&gt;Results and reruns&lt;/h2&gt;
&lt;p&gt;Since Robinhood is a live application, I didn&amp;rsquo;t run the test— it&amp;rsquo;s never a good idea to load test a domain you don&amp;rsquo;t own when you&amp;rsquo;re talking about thousands of users. However, &lt;a href="https://api.flood.io/pfQuAGz3"&gt;here&amp;rsquo;s a shareable link&lt;/a&gt; to another small Element test I ran previously.&lt;/p&gt;
&lt;p&gt;Real-time dashboards like that one on Flood allow you to get a quick look into how the load test is running and to make any changes if necessary. When testing for FOMO, I think they&amp;rsquo;re invaluable. For example, during a stress test where users are added at regular intervals, a real-time dashboard would help you identify increases in response time as a result of the increased number of users. It would also let you monitor the test and stop it when necessary.&lt;/p&gt;
&lt;p&gt;From the link above, click on the right arrow on the lone transaction.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-13.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;That will take you to the Transaction Detail page. Remember those screenshots from Element? If you have any in your script, you&amp;rsquo;ll see them here, arranged according to time.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200309-14.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This lets you troubleshoot issues during FOMO testing and react quickly.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Even with sound load testing strategies in place, it&amp;rsquo;s challenging to plan for the massive spike in traffic that FOMO can bring, yet it&amp;rsquo;s necessary to do so. FOMO, as a phenomenon, is only going to increase in magnitude as social networks make it easier to spread fear, uncertainty, and doubt as well as information.&lt;/p&gt;
&lt;p&gt;We may not be able to determine precisely what will break, or when, but what we can do is put systems into place to plan for it. We can assess an application&amp;rsquo;s susceptibility to FOMO and plan for end-to-end performance engineering of the system as a whole. We can start with the assumption that applications will fail and then determine the most likely candidates for that failure. We can routinely expose systems to traffic and circumstances that are extraordinary, and in doing so, we can improve our preparedness for something like FOMO.&lt;/p&gt;
&lt;p&gt;FOMO is irrational and unpredictable— but that doesn&amp;rsquo;t mean it needs to be unexpected.&lt;/p&gt;</description></item><item><title>Load Testing Lessons from the Iowa Caucus</title><link>https://nicolevanderhoeven.com/blog/20200306-load-testing-lessons/</link><pubDate>Fri, 06 Mar 2020 21:59:02 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200306-load-testing-lessons/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/load-testing-lessons-from-the-iowa-caucus"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/load-testing-lessons-from-the-iowa-caucus"&gt;here&lt;/a&gt; and &lt;a href="https://thenewstack.io/how-id-load-test-the-iowa-caucus-app/"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR? Here are the highlights in this article:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Shadow, Inc. created a mobile app for the Iowa Caucus in February 2020, and the app failed, causing reported results to be delayed.
The app reported different vote totals due to a system integration bug where the output was in a different format than what it needed to be.
Here are the steps to load testing a mobile app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Test planning: Does the app require load testing? If yes, what is the underlying technology? How do we create a workload model?&lt;/li&gt;
&lt;li&gt;Scripting: Which mobile load testing tool should we use? How do we record traffic from a mobile device?&lt;/li&gt;
&lt;li&gt;Execution: What load test scenarios should we have? How can we scale up the load generated?
Conclusion: While the Iowa Reported App failure was not due entirely to load testing, load testing could have helped make it more robust.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;A company called Shadow, Inc. made headlines earlier this week when they developed an app to count and report votes during the Iowa Democratic Caucus in the US. This app, called IowaReporterApp, ended up failing on several fronts, resulting in a &lt;a href="https://www.nytimes.com/2020/02/06/upshot/iowa-caucuses-errors-results.html"&gt;days-long delay&lt;/a&gt; in publishing the total votes, during which manual methods had to be employed.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200306-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://twitter.com/ShadowIncHQ/status/1224773796307050497"&gt;@ShadowIncHQ on Twitter&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This delay led to significant criticism from the software testing industry as well as from the general public. Some developers raised questions about the &lt;a href="https://www.vice.com/en_us/article/3a8ajj/an-off-the-shelf-skeleton-project-experts-analyze-the-app-that-broke-iowa"&gt;code quality of the app&lt;/a&gt;. The app &lt;a href="https://twitter.com/iowademocrats/status/1225170253778444291"&gt;inaccurately reported voting totals&lt;/a&gt; that differed from the Democratic Party&amp;rsquo;s own manual reports, suggesting an issue with functional testing, particularly system integration testing. Shadow&amp;rsquo;s &lt;a href="https://thehill.com/policy/cybersecurity/481409-dhs-chief-says-offer-to-vet-iowa-caucus-app-was-declined"&gt;refusal to accept the Department of Homeland Security&amp;rsquo;s help with security testing&lt;/a&gt; also raised eyebrows. What was perhaps the most damning, though, was the question of &lt;a href="https://twitter.com/zeynep/status/1224545350364672001"&gt;whether such an app was necessary in the first place&lt;/a&gt;, when cheaper and more effective solutions existed.&lt;/p&gt;
&lt;p&gt;What I&amp;rsquo;m most interested in, however, are the load issues that the app faced. Let&amp;rsquo;s rewind a little to before the caucus. How would I have load tested an app like the IowaReporterApp?&lt;/p&gt;
&lt;h2 id="planning-a-mobile-load-testing"&gt;Planning a mobile load testing&lt;/h2&gt;
&lt;p&gt;One of the first things to figure out when planning a load test is whether a load test is even necessary.&lt;/p&gt;
&lt;h3 id="do-you-even-need-to-load-test-your-mobile-app"&gt;Do you even need to load test your mobile app?&lt;/h3&gt;
&lt;p&gt;To answer that, I had to find out more about the app.&lt;/p&gt;
&lt;p&gt;In the caucus, residents of Iowa who registered themselves as Democratic voters gathered to vote on whom they thought should be the party&amp;rsquo;s candidate for the presidential elections later this year.&lt;/p&gt;
&lt;p&gt;There are a few things that I think make this app the perfect candidate for load testing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;It was highly publicized.&lt;/strong&gt; The caucuses would always have been an event of international interest, but the hype surrounding electronic voting increased media attention on the caucus even further.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;It was mission-critical&lt;/strong&gt;. The precinct chairs would use the app as the primary device for capturing votes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;It needed to handle significant simultaneous load&lt;/strong&gt;. There were &lt;a href="https://en.wikipedia.org/wiki/2020_Iowa_Democratic_caucuses"&gt;over a thousand precinct caucuses&lt;/a&gt; organized, each with a precinct chair that would be using the app.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For these reasons, the answer is a resounding &amp;ldquo;Yes.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="what-type-of-app-is-it"&gt;What type of app is it?&lt;/h3&gt;
&lt;p&gt;The best way to learn about an app is to ask its developers. In this case, I opted to do a little research on my own by &lt;a href="https://www.vice.com/en_us/article/z3b3g9/here-is-a-link-to-the-shadow-inc-app-that-blew-up-the-iowa-caucus"&gt;downloading the .apk file that Motherboard (Vice) made available&lt;/a&gt; and looking through the code myself.&lt;/p&gt;
&lt;p&gt;The app is a mobile-only app for Android and iOS devices, written mostly using Facebook&amp;rsquo;s React Native package. The primary function of the app was to sent the vote totals to Shadow Inc.&amp;rsquo;s &lt;a href="https://cloud.google.com/functions/?utm_source=google&amp;amp;utm_medium=cpc&amp;amp;utm_campaign=emea-nl-all-en-dr-bkws-all-all-trial-e-gcp-1008073&amp;amp;utm_content=text-ad-none-any-DEV_c-CRE_253510459861-ADGP_Hybrid+%7C+AW+SEM+%7C+BKWS+~+EXA_M:1_NL_EN_General_Cloud+Functions_ETL+Warehouse-KWID_43700019207359819-kwd-135583196912-userloc_9065312&amp;amp;utm_term=KW_google%20functions-ST_google+functions&amp;amp;ds_rl=1242853&amp;amp;ds_rl=1245734&amp;amp;ds_rl=1245734&amp;amp;gclid=EAIaIQobChMI6bKKzNi_5wIVk-R3Ch04HA1XEAAYASAAEgKTcPD_BwE"&gt;Google Cloud Functions&lt;/a&gt; server.&lt;/p&gt;
&lt;p&gt;The technology involved is worth noting because it can help determine which load testing tool to use. Since the Iowa Reporter App, like most mobile apps, sends requests to a RESTful API, JMeter or Gatling, both well-tested, robust, and open-source protocol-level tools with large user bases, were perfect for the job. I decided to go with JMeter, my personal tool of choice.&lt;/p&gt;
&lt;h3 id="how-do-we-create-a-workload-model"&gt;How do we create a workload model?&lt;/h3&gt;
&lt;p&gt;Now let&amp;rsquo;s talk about the workload model, which is a plan for how the load we generate needs to look. The objective here is to simulate the load that matches the expected real-life load as closely as possible.&lt;/p&gt;
&lt;p&gt;At this point, I would typically look at historical data to try to see the amount of load and the load profile. If I had metrics on the server traffic after previous releases of the app using something like Google Analytics or an APM tool, I would have used that data as a starting point for the workload model of this release. However, in this case, the app hadn&amp;rsquo;t been publicly released before, so I needed to make some educated guesses.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/2020_Iowa_Democratic_caucuses"&gt;According to Wikipedia&lt;/a&gt;, there were a total of 1,777 Iowa precinct caucuses, of which 99 were global satellite caucuses set up for Iowa residents temporarily not able to attend the local caucuses.&lt;/p&gt;
&lt;p&gt;In a Democratic caucus, the actual voting process generally takes &lt;a href="https://www.theguardian.com/us-news/2020/feb/03/iowa-caucuses-2020-elections-candidates"&gt;about two hours&lt;/a&gt;. In the Iowa caucus, the precinct chairs would have used the app twice - once for each round of voting. Around the end of the two hours, each chair&amp;rsquo;s app would have sent the main payload, containing total votes for each candidate, to the backend server. All this is done simultaneously across all 1,777 precincts.&lt;/p&gt;
&lt;p&gt;At a minimum, there are two main events here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The login - Precinct chairs needed to log in to the app, which would presumably have happened around the same time across all precincts towards the beginning of the voting process, because the app also contained instructions on how to run a caucus.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The submission - This is the transmission of the total vote counts for each candidate, sent to Google Cloud Functions, and would also have happened around the same time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&amp;rsquo;s possible that the app also sent other requests between these two events, but since most of the app is inaccessible without a valid login, I&amp;rsquo;ll focus on the initial login request.&lt;/p&gt;
&lt;p&gt;In a situation like this where I don&amp;rsquo;t have previous data to show how spread out those requests was, it&amp;rsquo;s prudent to err on the side of caution. Assuming the precinct chairs triggered those requests within 10 minutes of each other and rounding up the number of requests for some wiggle room, I estimated that there would have been 1,800 login requests within 10 minutes.&lt;/p&gt;
&lt;p&gt;I decided to test this as a gradual ramp-up of users within 10 minutes.&lt;/p&gt;
&lt;h2 id="scripting-for-mobile"&gt;Scripting for mobile&lt;/h2&gt;
&lt;p&gt;Okay, time to write the script. Since I didn&amp;rsquo;t have the luxury of being able to ask the developers for the format of the login request, I did the next best thing and tried to find out myself by recording the app traffic in action.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200306-02.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="recording-mobile-traffic-on-a-laptop-with-jmeter"&gt;Recording mobile traffic on a laptop with JMeter&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s how I was able to see the request from the mobile app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I downloaded the app to a spare Android mobile by using the APK link above.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I set up an HTTP(S) Test Script Recorder on JMeter on my laptop with the port 8888. I also made sure my laptop was connected to my wifi network.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I set up my mobile to use a proxy using my laptop&amp;rsquo;s local IP address on the network and port 8888.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, I went back to JMeter on my laptop and started the HTTP(S) Test Script Recorder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, I navigated to the app, typed in flood.io in the Precinct ID field (obviously not a real ID), and clicked the &amp;ldquo;Click here to log in&amp;rdquo; button.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s the traffic as seen on JMeter&amp;rsquo;s View Results Tree listener afterward:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200306-03.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;And here&amp;rsquo;s the recorded request within the recording controller:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200306-04.png" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="analyzing-the-request"&gt;Analyzing the request&lt;/h3&gt;
&lt;p&gt;It looks like at least the initial login request is a GET to an Auth0 service, with several parameters being passed in the body. It also has some authorization headers.&lt;/p&gt;
&lt;p&gt;The parameter values also look like they might be dynamic, meaning they change from session to session. At this point, I would typically change some of these from being hardcoded values into dynamic ones so that the application receives a request in the right format, and I could look through &lt;a href="https://auth0.com/docs/api/authentication"&gt;Auth0&amp;rsquo;s API documentation&lt;/a&gt; to find out how the authentication message should be formulated.&lt;/p&gt;
&lt;p&gt;Since I don&amp;rsquo;t have a valid login for testing purposes, I skipped this step and just used the raw request for demonstration purposes.&lt;/p&gt;
&lt;h3 id="preparing-to-run"&gt;Preparing to run&lt;/h3&gt;
&lt;p&gt;Think time is a delay between requests meant to simulate the time it takes for a user to think before taking another action. Without think time, JMeter would fire off requests one after another, which often isn&amp;rsquo;t realistic and drains resources. I don&amp;rsquo;t want that, so I added a JMeter timer. There are plenty of timers available, but I settled on a Uniform Random Timer.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.vice.com/en_us/article/3a8ajj/an-off-the-shelf-skeleton-project-experts-analyze-the-app-that-broke-iowa"&gt;The CEO of Shadow told Motherboard&lt;/a&gt; that many precinct chairs were confused by the login process as they had to put in three strings of numbers in the right sequence. So, I decided to work that into the script with the timer. I set it up with a constant delay of 2 minutes and a random delay of 1 minute so that the delay would vary from 2 minutes to 3 minutes to simulate a precinct chair attempting to log in a few times.&lt;/p&gt;
&lt;h2 id="execution-running-your-mobile-load-test"&gt;Execution: Running your mobile load test&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.vice.com/en_us/article/z3b3g9/here-is-a-link-to-the-shadow-inc-app-that-blew-up-the-iowa-caucus"&gt;According to Motherboard&lt;/a&gt;, Shadow has already taken the app servers offline. However, it&amp;rsquo;s not a good idea to run a full load test against an endpoint that you don&amp;rsquo;t own, so I decided not to run a test.&lt;/p&gt;
&lt;p&gt;Usually, though, I’d want to start with a shakeout test of 10 users or so, and then gradually increase the user load until the peak of 1,800 users. I also would have played around with changing the load profile, such as by increasing or decreasing the ramp-up or steady-state durations, experimenting with stress tests and soak tests, and adding traffic from another availability zone to simulate the 99 satellite precincts around the world. Running multiple load generators can quickly get out of hand if you want to use physical machines, so I would run load tests on the cloud with something like &lt;a href="https://flood.io/"&gt;Flood&lt;/a&gt;. All of these little changes would have given valuable data that would help me gauge the app’s performance in production-like situations.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Building quality software is not a trivial task. There are many moving parts to account for, and critical errors can be introduced in every stage of an app&amp;rsquo;s development. While load issues were not the main culprit in the failure of the IowaReporterApp, a simple load test like the one I describe here, might have improved app stability and reliability. Even in cases where load testing doesn&amp;rsquo;t seem necessary, it can still uncover gross issues in configuration, usability, or security for an app developed on a deadline.&lt;/p&gt;</description></item><item><title>How AirTasker uses Ruby JMeter and Flood for their load testing</title><link>https://nicolevanderhoeven.com/blog/20200224-airtasker/</link><pubDate>Mon, 24 Feb 2020 18:23:31 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200224-airtasker/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/how-airtasker-uses-ruby-jmeter-and-flood-for-their-load-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/how-airtasker-uses-ruby-jmeter-and-flood-for-their-load-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the recent RubyConf 2020 held in Melbourne, Australia, Nancy Cai from AirTasker talked about how they built a performance testing framework using Ruby JMeter and Flood. Luckily, we had some pleasantly surprised Flooders in attendance to take notes during this fantastic talk!&lt;/p&gt;
&lt;p&gt;AirTasker is a platform allowing the outsourcing of everyday tasks. When discussing the reasons that AirTasker decided they wanted to carry out performance and load testing, Nancy told a fascinating story about an overly successful marketing campaign—a good problem to have, but a problem nonetheless. The Sydney Cats and Dogs Home, an animal shelter, teamed up with AirTasker to pay one lucky person $500 to spend the day playing with the resident animals. The prospect of this dream job created an enormous surge in AirTasker&amp;rsquo;s traffic that caused alarm bells to go off on the underlying application servers, resulting in an end-user experience that was less than desired.&lt;/p&gt;
&lt;p&gt;Nancy included this hilarious slide about some common feelings regarding application performance.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200224-01.png" alt="A common feeling when application performance is brought up" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="steps-for-running-a-performance-test-the-airtasker-way"&gt;Steps for running a performance test, the AirTasker way&lt;/h2&gt;
&lt;p&gt;To avoid repeating that mistake, Nancy and her team set out to run performance testing by using the following simple steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Determine performance criteria.&lt;/li&gt;
&lt;li&gt;Configure the test environment to be as production-like as possible.&lt;/li&gt;
&lt;li&gt;Plan and design based on user behavior, including preparing test data.&lt;/li&gt;
&lt;li&gt;Implement test design and set up the framework (which in AirTasker&amp;rsquo;s case involved Ruby JMeter as the performance testing tool and Flood as a load testing solution).&lt;/li&gt;
&lt;li&gt;Run test.&lt;/li&gt;
&lt;li&gt;Analyze and fine-tune results.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="setting-up-a-load-testing-tool-and-framework"&gt;Setting up a load testing tool and framework&lt;/h2&gt;
&lt;p&gt;When it came to selecting the right tools for the job, AirTasker wanted tools that met the following criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ability to simulate production-like traffic easily&lt;/li&gt;
&lt;li&gt;Widespread use in the industry&lt;/li&gt;
&lt;li&gt;Ease of creation and maintenance of tests for everyone&lt;/li&gt;
&lt;li&gt;Historical results aggregation&lt;/li&gt;
&lt;li&gt;Visualization of performance metrics&lt;/li&gt;
&lt;li&gt;Integration with CI and monitoring tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nancy&amp;rsquo;s team compared several load testing tools and found they enjoyed using the Apache JMeter tool, which is a popular open source load testing software. However, they quickly ran into an &amp;ldquo;out of memory&amp;rdquo; error after attempting to run about 400-800 concurrent users on a single machine. She and her team decided to use a distributed load testing platform to run the load they required to scale up more effectively.&lt;/p&gt;
&lt;p&gt;Enter Flood.&lt;/p&gt;
&lt;h2 id="why-airtasker-chose-flood"&gt;Why AirTasker chose Flood&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200224-02.png" alt="JMeter, Flood, and the Ruby JMeter gem combination is a winner" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We also chose Flood.io [&amp;hellip;] because it comes in with a really good open-source gem called Ruby JMeter. It is a Ruby-based DSL for building JMeter test plans. - Nancy Cai, AirTasker&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nancy and her team enjoyed the benefits of being able to write load tests natively in Ruby and then using Flood&amp;rsquo;s Ruby JMeter gem, which is a Domain-Specific Language (DSL). Flood allowed them to turn their Ruby scripts into .jmx files, which is the native XML format that JMeter uses.&lt;/p&gt;
&lt;p&gt;When they were ready to scale up, AirTasker ran the same JMeter script in Flood.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200224-03.png" alt="Sample of a report on Flood from a JMeter test" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The real-time Flood dashboard allowed them to see that there was a problem on one of the API endpoints, called &amp;ldquo;Get pet reviews.&amp;rdquo; Further investigations revealed a performance issue due to a long-running query against a database server in the code, which AirTasker&amp;rsquo;s developers could fix.&lt;/p&gt;
&lt;h2 id="best-practices"&gt;Best practices&lt;/h2&gt;
&lt;p&gt;Nancy gives some great advice for anyone running a performance test, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;asking the right questions as a load tester&lt;/li&gt;
&lt;li&gt;running baseline tests&lt;/li&gt;
&lt;li&gt;being aware of the difference between your test and production environments&lt;/li&gt;
&lt;li&gt;keeping in mind the caching and autoscaling mechanisms that may drastically change the results&lt;/li&gt;
&lt;li&gt;the differences between stress testing and other types of performance testing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We were so thrilled to hear Nancy&amp;rsquo;s story on how Flood helped AirTasker run their performance tests. Catch the entirety of Nancy&amp;rsquo;s talk below, and check out the resource links at the end of the post to follow Nancy&amp;rsquo;s story.‍&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Q09wJm4P8Xw?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/flood-io/ruby-jmeter"&gt;Flood&amp;rsquo;s Ruby JMeter gem&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nancy-cai/performance-test-ruby-jmeter"&gt;Nancy&amp;rsquo;s GitHub repo&lt;/a&gt; where you can find a copy of her test scripts&lt;/p&gt;
&lt;p&gt;Follow Nancy on &lt;a href="https://twitter.com/nancycai8"&gt;Twitter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Follow Nancy on &lt;a href="https://github.com/nancy-cai"&gt;GitHub&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Mobile Load Testing on the Cloud</title><link>https://nicolevanderhoeven.com/blog/20200221-mobile-load-testing-on-the-cloud/</link><pubDate>Fri, 21 Feb 2020 18:44:58 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20200221-mobile-load-testing-on-the-cloud/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/mobile-load-testing-on-the-cloud"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/mobile-load-testing-on-the-cloud"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="what-is-mobile-load-testing"&gt;What is mobile load testing?&lt;/h2&gt;
&lt;p&gt;What is the difference between load testing web apps and mobile apps? In general, your goal is the same: to improve app performance by systematically exposing your app to the load you expect, whether it&amp;rsquo;s on the web or on a mobile device. However, while the purpose may be the same, there a few extra things to consider when it comes to mobile app performance, and it&amp;rsquo;s easy to make some key mistakes. In this article, I&amp;rsquo;ll give you a little bit of the theory behind mobile load testing and then show you practical steps to script and execute a load test.&lt;/p&gt;
&lt;h2 id="challenges-in-mobile-load-testing"&gt;Challenges in mobile load testing&lt;/h2&gt;
&lt;p&gt;If you have a web app that is mobile first, and your users typically access your site through a browser on their mobile devices as well as on their laptops, you have more choices for optimizing mobile performance. You can use browser-based load testing tools as well as protocol-level load testing tools to generate load.&lt;/p&gt;
&lt;p&gt;If you have a native mobile app that you&amp;rsquo;d like to load test, though, browser-based load testing tools are a little more complicated. One of the most common approaches to mobile load testing is to use some type of device farm. A device farm is a collection of mobile devices and a way to orchestrate and control them so that they access an application simultaneously. Here&amp;rsquo;s why they&amp;rsquo;re not the best choice for load testing.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-01.jpg" alt="Fragmentation can make mobile load testing difficult" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="device-fragmentation"&gt;Device fragmentation&lt;/h3&gt;
&lt;p&gt;One of the biggest challenges is mobile device fragmentation. Not only are there a lot of mobile devices, but there are also many operating systems and versions. Even narrowing the pool to Android and ios leaves us with a large selection. A common mistake in mobile load testing is trying to account for these hundreds of combinations.&lt;/p&gt;
&lt;h3 id="logistics"&gt;Logistics&lt;/h3&gt;
&lt;p&gt;Another challenge is the logistics of controlling physical devices with different operating systems, which is not trivial. Unless you have a thousand friends that you can rope into doing this at the same time, you&amp;rsquo;ll need to pay for some sort of service that can orchestrate this for you.&lt;/p&gt;
&lt;h3 id="cost"&gt;Cost&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s also the fact that this process is resource-intensive. Even if your device farm consists of emulators on virtual machines rather than physical mobile devices, that still uses up a lot of CPU and memory, and you&amp;rsquo;d need to spin up dozens or hundreds of machines.&lt;/p&gt;
&lt;p&gt;Luckily, this type of mobile load testing is not the only option available to us.&lt;/p&gt;
&lt;h2 id="better-strategies-for-mobile-load-testing"&gt;Better strategies for mobile load testing&lt;/h2&gt;
&lt;p&gt;A better option for back-end performance testing or load testing your mobile application is by using API load testing, which works on the protocol level. To understand this, let&amp;rsquo;s talk about what aspects of software we are analyzing when we test for &amp;ldquo;performance.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We can generally break up performance testing into two types: front-end and back-end. Both types have the goal of making the app respond faster to end-users, but they do so by optimizing different parts of the application.&lt;/p&gt;
&lt;h3 id="front-end-performance"&gt;Front-end performance&lt;/h3&gt;
&lt;p&gt;Front-end performance is primarily concerned with the UI of the app. A front-end performance tester optimizes page resources (like CSS, images, JS) to render faster on the page. Since a mobile app runs on a user&amp;rsquo;s smartphone or another mobile device, its front-end performance would also be affected by things we can&amp;rsquo;t control, such as the operating system and version of the mobile or its physical configuration (CPU and memory). A front-end issue for a particular mobile device type might not necessarily affect performance on another. Device farms can be good at measuring front-end performance.&lt;/p&gt;
&lt;h3 id="back-end-performance"&gt;Back-end performance&lt;/h3&gt;
&lt;p&gt;In contrast, back-end performance focuses on the underlying mobile application servers. A back-end performance tester optimizes the things behind the interface, and it is affected by things like the size of the responses that a server sends to a user, server configuration, application infrastructure, and how downstream servers talk to each other. These factors may be invisible to a typical end-user. A performance bottleneck in the back-end is likely to affect all users of an app to some degree.&lt;/p&gt;
&lt;h3 id="load-testing-for-back-end-performance"&gt;Load testing for back-end performance&lt;/h3&gt;
&lt;p&gt;Load testing is about determining how your mobile application behaves in response to traffic, so it targets the back-end components of the application, not the front-end.&lt;/p&gt;
&lt;p&gt;Both front-end and back-end performance are necessary to get a holistic view of mobile performance. Still, &lt;strong&gt;if you want to load test your mobile app, you need to generate traffic on your application &lt;em&gt;server&lt;/em&gt;, and not necessarily to drive the app on real devices&lt;/strong&gt;. Understanding this opens up options for mobile load testing.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-02.jpg" alt="A data center can be out of most testing teams’ reach" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="api-load-testing"&gt;API load testing&lt;/h2&gt;
&lt;p&gt;Most native mobile apps work by using a RESTful API to send instructions to the application server. Luckily for us, there&amp;rsquo;s no magic in sending these types of requests from a smartphone— we can simulate this easily by using an API load testing tool to send these requests on a non-mobile device too.&lt;/p&gt;
&lt;p&gt;Here are some advantages of using API load testing tools to test your mobile app instead of using a device farm:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It&amp;rsquo;s easier to set up and maintain&lt;/strong&gt;: API load testing can be easier because you don&amp;rsquo;t have to account for changes in the user interface as long as the underlying API calls are the same.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It&amp;rsquo;s cost-effective&lt;/strong&gt;: You can run about 1,000 users for an API test on a single machine (we used AWS m5.xlarge machines to arrive at this estimate). This translates into fewer resources that you&amp;rsquo;ll need to pay for whether you&amp;rsquo;re running a thousand users or [a million](whether you&amp;rsquo;re running a thousand users or a million), and, ultimately, a lower cost.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It lets you narrow your load testing scope&lt;/strong&gt;: If you have a particular endpoint that has historically shown performance issues, you can use API load testing to target just that one endpoint. On a device farm, you would have to create a test automation script that might click a button and send dozens of requests, only one of which you actually wanted to test.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tools-for-mobile-load-testing"&gt;Tools for mobile load testing&lt;/h3&gt;
&lt;p&gt;You can use many tools for API load testing, but you can&amp;rsquo;t go wrong with robust open-source tools like JMeter and Gatling. Both tools are well-used tools in the performance industry, and they also have large user bases and ample documentation for helping anyone get started. In this article, I&amp;rsquo;ll continue with JMeter specifically.&lt;/p&gt;
&lt;p&gt;How do you get started with writing your script? Ideally, your developers should be able to point you to the API documentation for your app or be able to describe the format of the request themselves. But what if you wanted to get started on your own?&lt;/p&gt;
&lt;h2 id="how-to-record-network-traffic-from-a-native-app-with-apache-jmeter"&gt;How to record network traffic from a native app with Apache JMeter&lt;/h2&gt;
&lt;p&gt;Recently I wrote &lt;a href="https://thenewstack.io/how-id-load-test-the-iowa-caucus-app/"&gt;an article on The New Stack&lt;/a&gt; about how I would have load tested the IowaReporterApp, which was responsible for delays in reported vote totals for the 2020 Iowa Democratic Caucuses. To show how to load test that app, I created an actual load testing script for that article, without any input from the app developers. I chose to script using Apache JMeter, a popular open source API load testing tool, but Gatling (also open source) is another good option.&lt;/p&gt;
&lt;p&gt;Here are the steps I took on my Android mobile, a Samsung Galaxy Note 10+. The same principles should apply to iOS devices and other Android devices.&lt;/p&gt;
&lt;h3 id="1-download-and-install-the-app"&gt;1. Download and install the app.&lt;/h3&gt;
&lt;p&gt;Even if the app isn&amp;rsquo;t on the Google Play store, you can install an app using the .apk file. You may have to enable Developer Mode on your phone, which is not the same as jailbreaking. All this does is bypass the security feature that would otherwise not allow you to install apps that don&amp;rsquo;t come from the Play store.&lt;/p&gt;
&lt;p&gt;To enable Developer Mode, go to Settings &amp;gt; About phone &amp;gt; Software information. Then, scroll down to the field Build number and tap it seven times. You should see a confirmation message that Developer mode is on.&lt;/p&gt;
&lt;p&gt;Now download the .apk, click on it, and follow the prompts to install it.&lt;/p&gt;
&lt;h3 id="2-set-up-an-https-test-script-recorder-on-jmeter"&gt;2. Set up an HTTP(S) Test Script Recorder on JMeter&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll need to have JMeter set up already for this step.&lt;/p&gt;
&lt;p&gt;Connect your laptop to a wi-fi network.&lt;/p&gt;
&lt;p&gt;From a new test plan on JMeter, right-click on Test Plan and then click on Add &amp;gt; Non-Test Elements &amp;gt; HTTP(S) Test Script Recorder. Click on it and take note of the port it uses in Global Settings.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-03.png" alt="Checking the port in the JMeter HTTPS Test Script Recorder" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Next, right-click on the test recorder element you just created and click Add &amp;gt; Listener &amp;gt; View Results Tree. This element shows details of requests and responses when we run tests.&lt;/p&gt;
&lt;p&gt;Then, right-click on Test Plan again and click Add &amp;gt; Threads &amp;gt; Thread Group.&lt;/p&gt;
&lt;p&gt;Finally, right-click on Thread Group and click Add &amp;gt; Logic Controller &amp;gt; Recording Controller.&lt;/p&gt;
&lt;p&gt;Your test plan should look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-04.png" alt="JMeter Test Plan containing a Recording Controller and an HTTPS Test Script Reocrder" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="3-set-up-your-mobile-to-use-a-proxy"&gt;3. Set up your mobile to use a proxy.&lt;/h3&gt;
&lt;p&gt;On your mobile, connect to the same wireless network that your laptop is connected to.&lt;/p&gt;
&lt;p&gt;Go back to your laptop and find out its local IP address. On a Mac, you can see this by clicking on the Apple logo &amp;gt; System Preferences &amp;gt; Network &amp;gt; Advanced &amp;gt; TCP/IP. The IPv4 Address is what you&amp;rsquo;re looking for, and it might look something like 192.168.86.125.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-05.png" alt="Getting your local IP address on a Mac" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;On your mobile, go to Settings &amp;gt; Connections &amp;gt; Wi-Fi. Under Current network, click on the settings wheel next to your wireless network. Click on Advanced.&lt;/p&gt;
&lt;p&gt;Enter your computer&amp;rsquo;s local IP address in the Proxy host name field.&lt;/p&gt;
&lt;p&gt;In the Proxy port field, enter 8888, or the port that was displayed in the HTTP(S) Test Script Recorder on JMeter.&lt;/p&gt;
&lt;p&gt;Click Save.&lt;/p&gt;
&lt;h3 id="4-start-recording-traffic"&gt;4. Start recording traffic.&lt;/h3&gt;
&lt;p&gt;On JMeter, click on the HTTP(s) Test Script Recorder element and click Start.&lt;/p&gt;
&lt;p&gt;On your mobile, open the app and log in or perform the action that you&amp;rsquo;d like to record.&lt;/p&gt;
&lt;p&gt;As you use the app, you should be able to see the requests coming in on JMeter in the View Results Tree:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-06.png" alt="View Results Tree listener view of traffic coming from the proxy" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;If you click on the Recording Controller, you&amp;rsquo;ll also see the exact format of the requests that the app made:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-07.png" alt="Seeing parameters sent with a recorded request in JMeter" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="scripting-and-troubleshooting-your-mobile-load-testing-script"&gt;Scripting and troubleshooting your mobile load testing script&lt;/h2&gt;
&lt;p&gt;What you now have is a recording of the mobile traffic you need to simulate. If you stop here and try to run it, you may find that it fails. That&amp;rsquo;s because you haven&amp;rsquo;t done any scripting yet.&lt;/p&gt;
&lt;p&gt;Here are a few things you&amp;rsquo;ll need to do to turn a recording into a script.&lt;/p&gt;
&lt;h3 id="add-assertions"&gt;Add assertions&lt;/h3&gt;
&lt;p&gt;An assertion is a way to verify that the request is returning the response that you expect. JMeter only marks a request as failed when it returns an HTTP 4xx or HTTP 5xx response code. However, there are times when the HTTP response code returned is an HTTP 200 OK, but the page that was returned was still not the one you expected. Without assertions, JMeter would mark this request as passed, yielding false positives.&lt;/p&gt;
&lt;p&gt;For instance, if you&amp;rsquo;re sending a login request, then a good assertion for the response to that request might be text like &amp;ldquo;Welcome, Nicole&amp;rdquo; or &amp;ldquo;Logged in.&amp;rdquo; To see the data that should be returned, you can either run through the action manually in your browser and use Developer Tools to inspect the response or click on the response tab in the View Results Tree listener in JMeter from your recording.&lt;/p&gt;
&lt;p&gt;Assertions also allow you to pinpoint more accurately where in your script the error is.&lt;/p&gt;
&lt;h3 id="replace-hardcoded-values-with-dynamic-values"&gt;Replace hardcoded values with dynamic values&lt;/h3&gt;
&lt;p&gt;The last screenshot above shows the parameters that the mobile application sends to the server to log in. If you examine the parameter values, you&amp;rsquo;ll notice that several of them are long strings. The script fails when you just send the same request again because some of these are dynamic. This means that parts of the request need to change before your device is allowed to access the application. You might need to add a timestamp, the right answer to a challenge, a session or client ID, or codes returned in previous requests. These dynamic values help the server determine whether the login request is a valid login attempt.&lt;/p&gt;
&lt;p&gt;This is where help from a developer would come in handy because he or she can tell you how each parameter value needs to be formed.&lt;/p&gt;
&lt;h3 id="add-think-time"&gt;Add think time&lt;/h3&gt;
&lt;p&gt;Think time is a delay between requests that simulates the pauses that a real user takes to read text on the page or otherwise interact with your application. Most performance testing tools do this. In JMeter, they are called timers, and there are several to choose from.&lt;/p&gt;
&lt;p&gt;I like the Uniform Random Timer because it allows you to set a constant value for the delay as well as an additional random delay on top of it. I recommend having a variable delay because setting a completely constant delay makes the test too regular and causes unnaturally high load at the interval you set. Real users don&amp;rsquo;t coordinate with each other, so stagger your requests by using a variable component to your think time.&lt;/p&gt;
&lt;h3 id="debug-your-test-script"&gt;Debug your test script&lt;/h3&gt;
&lt;p&gt;Before you think about scaling up, run your test a few times with one user on your local machine. If you don&amp;rsquo;t get any errors, try other configurations such as running two sequential users and running two threads at the same time. This way, you can test any correlations you&amp;rsquo;ve had to make for those dynamic values.&lt;/p&gt;
&lt;p&gt;It may be tempting to skip this step, but it&amp;rsquo;s necessary before scaling up your load test.&lt;/p&gt;
&lt;h3 id="set-test-parameters"&gt;Set test parameters&lt;/h3&gt;
&lt;p&gt;Test parameters are settings like the number of threads, ramp-up, and duration, which allow you to shape the pattern of the load you want to generate according to your test scenarios and test cases.&lt;/p&gt;
&lt;h4 id="number-of-concurrent-users-or-threads"&gt;Number of concurrent users or threads&lt;/h4&gt;
&lt;p&gt;This is the most obvious way to increase load. The more instances of your script running, the more requests are executed.&lt;/p&gt;
&lt;p&gt;You can change this in JMeter by clicking on your Thread Group and changing the “Number of Threads (users)” field:&lt;/p&gt;
&lt;p&gt;‍&lt;img src="https://nicolevanderhoeven.com/assets/20200221-08.png" alt="Changing the number of threads in JMeter" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h4 id="ramp-up"&gt;Ramp-up&lt;/h4&gt;
&lt;p&gt;In production, load on servers very rarely goes from 0 to 1000 in one second. Even for cases with a very definitive start time (such as an item going on sale at a particular time), the load generally increases gradually. You can simulate this gradual increase by adding ramp-up times in your script. A ramp-up is the amount of time during which new users are added at staggered intervals.&lt;/p&gt;
&lt;p&gt;Here’s an example of what that might look like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-09.png" alt="Changing the ramp-up of a test in JMeter" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h4 id="duration"&gt;Duration&lt;/h4&gt;
&lt;p&gt;Duration is how long the whole test lasts.&lt;/p&gt;
&lt;p&gt;The simplest way to set these values in JMeter is to use the basic Thread Group. You’ll need to change the fields highlighted below:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-10.png" alt="Changing test parameters in JMeter" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;To use the duration, you’ll have to select “Forever” next to Loop Count and “Scheduler” and type in the number of seconds in the Duration field. Otherwise, you can also run the script according to the number of loops, or iterations, that you want to run. However, since response times vary, using the Loop Count as a proxy for duration yields different results from test to test.&lt;/p&gt;
&lt;h2 id="running-a-shakeout-test-against-your-mobile-application"&gt;Running a shakeout test against your mobile application&lt;/h2&gt;
&lt;p&gt;When you&amp;rsquo;re ready to run on your load generators, resist the urge to jump straight to the peak load you want. This is a great way to incur unnecessary costs. Instead, run a small test with dozens of users. Running locally is for debugging issues in the script. The purpose of running a shakeout test is to spot errors elsewhere, such as in your test environment and your test data.&lt;/p&gt;
&lt;h2 id="baselining-your-mobile-application-performance"&gt;Baselining your mobile application performance&lt;/h2&gt;
&lt;p&gt;Finding a baseline is one of the primary goals you’ll have as you start executing load tests. To be able to assess how changes in the environment or code affect application performance, you’ll need a stable point of comparison. To accurately measure the effect of a variable (like a code change), it’s best to keep as much of the other circumstances surrounding the test as fixed as possible. The baseline is your &lt;strong&gt;unchanging&lt;/strong&gt; test scenario that you can use as a point of comparison to assess future performance.&lt;/p&gt;
&lt;p&gt;The application you&amp;rsquo;re scripting against, your tool of choice, and your script all affect your performance baseline, so do some experimenting to find out the setup that works best for your situation. This setup includes characteristics like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the number of concurrent users&lt;/li&gt;
&lt;li&gt;test duration&lt;/li&gt;
&lt;li&gt;think time, pacing, and all other waits and delays&lt;/li&gt;
&lt;li&gt;the script (including how requests are broken up into transactions)&lt;/li&gt;
&lt;li&gt;user density&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="user-density"&gt;User Density&lt;/h3&gt;
&lt;p&gt;User density is the number of threads that you can run on a node or a load generator. Figuring out user density it essential because all load generators, even virtual ones, have finite resources. Each load generator also has a finite amount of load that it can generate, based on its CPU and memory utilization, among other things. Trying to generate too many users on a single node may result in the node itself being the bottleneck for your test.&lt;/p&gt;
&lt;p&gt;To accurately assess your application’s performance, your load generators need to remain healthy during the test.&lt;/p&gt;
&lt;p&gt;It can be helpful to have a number to start with. At Flood, we’ve found that we can reliably run 1000 users using JMeter or Gatling with one of our AWS &lt;a href="https://aws.amazon.com/es/ec2/instance-types/"&gt;m5.xlarge machines&lt;/a&gt;. For reference, an m5.xlarge machine has 4 VCPUs and 16 GB RAM.&lt;/p&gt;
&lt;p&gt;If your machine is similarly specced, run a test on a single node with 1000 users. While the test runs, watch the CPU and memory utilization. If the test finishes without either of those consistently hitting over 80%, you’ll know that the node can handle that number of users.&lt;/p&gt;
&lt;p&gt;Let me reiterate, though, that you should use the 1000 users figure as a starting point only. You can then figure out your number through trial and error. Increase the number of users past 1000 and watch the resource utilization again. If that still looks good, add some more users and rerun. When you get to a test where the resource utilization hovers above 80%, stop and fall back to the previous number of users. You’ll now have your number.&lt;/p&gt;
&lt;p&gt;Another thing you might want to play with is the think time and pacing in your script. These waits tend to have a significant impact on resource utilization, so you can expect to be able to run more users per node if you increase your delays.&lt;/p&gt;
&lt;p&gt;If you’re using JMeter, always run your tests in non-GUI mode. GUI mode is great for debugging, but is unnecessarily resource-intensive for real load tests. While you’re at it, disable any listeners you may have that you don’t need to capture results.&lt;/p&gt;
&lt;p&gt;Figuring out the appropriate user density now prevents you from getting inaccurate test results. Running as many users as you can without overloading the load generators is also cost-effective, as you’ll be making sure that you provision only as many nodes as you need.&lt;/p&gt;
&lt;h2 id="scaling-up-your-load-test-on-the-cloud"&gt;Scaling up your load test on the cloud&lt;/h2&gt;
&lt;p&gt;It’s relatively simple to run a load test on one machine, but if you want to run on two or more machines, things quickly get unwieldy. Here are some options for achieving this.&lt;/p&gt;
&lt;h3 id="1-upload-your-tool-of-choice-and-script-to-every-machine-and-kick-off-each-test-separately"&gt;1. Upload your tool of choice and script to every machine and kick off each test separately.&lt;/h3&gt;
&lt;p&gt;Instead of running one large load test, think of this method as running several smaller ones at (about) the same time. This is relatively easy to set up, but the main disadvantage is that it’s tedious. You may also need licenses for your tools if you&amp;rsquo;re not using an open source one. You’ll want to ensure that each load generator is as similar as possible to the others in terms of operating system, tool version, and script version. Since each node kicks off a separate test, you’re not going to be able to see a real-time combined view of all the load tests, and after execution, you’ll need to retrieve results files individually and combine them.&lt;/p&gt;
&lt;h3 id="2-use-your-tools-distributed-testing-mode"&gt;2. Use your tool’s distributed testing mode.&lt;/h3&gt;
&lt;p&gt;JMeter has a feature that allows you to scale. This involves setting up agents on each load generator and using scripts to coordinate execution and results collection. This method requires a little bit more know-how and time to set up, but it’s a little more cohesive than the first method. Here are links on how to set this up: Remote Testing with JMeter&lt;/p&gt;
&lt;h3 id="3-use-a-load-testing-platform"&gt;3. Use a load testing platform.&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m admittedly biased, but if I were to go back to doing performance testing consulting work, I would choose Flood to run my tests. A platform like Flood that is built for distributed load testing is an excellent choice because it takes away all the setup considerations. This is the easiest option, especially for teams that are new to load testing or are perhaps less technical, because all the setup is done through a UI. Scaling out in this case just means uploading your script, choosing the number of nodes you want to run and in which region, and all the work is done for you in the background.&lt;/p&gt;
&lt;p&gt;While you can run on-premises, I would consider running your test cloud-based infrastructure for greater flexibility and ease of scaling unless you already have a data center.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20200221-11.jpg" alt="Running a load test on the cloud is the easiest way to get started with mobile load testing" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="what-happens-next"&gt;What happens next?&lt;/h2&gt;
&lt;p&gt;Running your test is just the beginning of mobile performance testing— there&amp;rsquo;s still metrics collection, results analysis, and resolving performance bottlenecks that you uncover. After all, it&amp;rsquo;s what you do with the test results that counts. Remember that performance and load testing, whether for web-based applications or mobile ones, are iterative. The real value comes from building the app performance testing principles discussed here into your release cycles, so that load testing becomes a habit rather than just a one-off exercise.&lt;/p&gt;</description></item><item><title>Load Testing Tutorial: Server Monitoring</title><link>https://nicolevanderhoeven.com/blog/20190926-load-testing-tutorial-server-monitoring/</link><pubDate>Thu, 26 Sep 2019 19:30:10 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20190926-load-testing-tutorial-server-monitoring/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/load-testing-tutorial-server-monitoring"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/load-testing-tutorial-server-monitoring"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It’s difficult to be a good load tester if you don’t like data, because a lot of the value of the load testing is in the analysis. Even if you successfully generate load against an application, it’s ineffective without enough data to determine how your application coped with the load. At &lt;a href="https://flood.io"&gt;Flood&lt;/a&gt;, we’re passionate about data because we know it drives decisions, so we’re always working to improve how we present data generated by our nodes. However, there’s another important source of data that in my experience is too often overlooked by new load testers: your own application servers.&lt;/p&gt;
&lt;p&gt;Executing a load test without monitoring server health is like flying blind: you’ll know when you land safely and you’ll know when you crash, but even if you do crash, you won’t know why—or how you can avoid it next time. Monitoring server health is the black box that will tell you what went wrong.&lt;/p&gt;
&lt;h2 id="what-server-metrics-should-you-monitor"&gt;What server metrics should you monitor?&lt;/h2&gt;
&lt;p&gt;There are a lot of metrics that you can monitor, and we always recommend that you choose what to measure based on your own specific circumstances, such as the &lt;a href="https://flood.io/blog/start-with-why-how-to-write-requirements-for-api-load-testing/"&gt;purpose for your testing&lt;/a&gt; or any previous outages you’ve had in the past. If you’re new to load testing, though, here’s a list of metrics you might consider measuring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Processor Time&lt;/strong&gt; - how much the processor is being utilised&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processor Interrupt Time&lt;/strong&gt; - how much time the processor is spending to handle interrupts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processor Privileged Time&lt;/strong&gt; - the time the processor spends handling overhead activities&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processor Queue Length&lt;/strong&gt; - the number of threads that are waiting to be executed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory (Available Bytes)&lt;/strong&gt; - unused memory available to process new requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory Cache Bytes&lt;/strong&gt; - the size of the memory cache, which is where copies of frequently accessed data are stored&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk I/O&lt;/strong&gt; - number of reads and writes to the disk during the test&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk Idle Time&lt;/strong&gt; - time that disks are not doing work&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk Transfer/sec&lt;/strong&gt; - average number of seconds that an I/O request takes to complete&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk Write/sec&lt;/strong&gt; - average number of seconds that a write request takes to complete&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network I/O&lt;/strong&gt; - bytes sent and received&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-do-you-determine-which-one-to-use"&gt;How do you determine which one to use?&lt;/h2&gt;
&lt;p&gt;If you’re still not sure where to start: &lt;strong&gt;At a minimum, you’ll need the CPU and memory utilisation&lt;/strong&gt; (in bold in the list above) of every major component you want to test. These two metrics are vital and if either of these is consistently maxing out at (or close to) 100%, that’s a sign that the component is struggling with the number of requests.&lt;/p&gt;
&lt;p&gt;As an aside, CPU and memory over-utilisation is a very common reason for less-than-ideal response times on the load generators, which is why we also show you those metrics on our nodes while you’re running the test.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20190926-01.png" alt="A snapshot of the server metrics provided for Flood’s servers during execution" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="how-can-you-monitor-these-metrics"&gt;How can you monitor these metrics?&lt;/h2&gt;
&lt;p&gt;How you get these metrics depends on your budget and your operating system. I’ll start with the free or lower cost methods and work my way up to enterprise solutions.&lt;/p&gt;
&lt;p&gt;If your servers are running Windows, &lt;a href="https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/perfmon"&gt;PerfMon&lt;/a&gt; is a good alternative. It’s built into Windows and the interface allows you to choose the counters that you want to measure and start recording.&lt;/p&gt;
&lt;p&gt;There’s also a JMeter &lt;a href="https://github.com/undera/perfmon-agent"&gt;server agent that you can download&lt;/a&gt;, confusingly called “PerfMon” but not to be mistaken with Windows’ PerfMon, that you can install on your (Windows or Linux) server and will gather metrics. It’s normally used in conjunction with the &lt;a href="https://jmeter-plugins.org/wiki/PerfMon/"&gt;PerfMon JMeter plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20190926-02.png" alt="Overview of the setup in the PerfMon JMeter plugin" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.nagios.org/projects/nagios-core/"&gt;Nagios Core&lt;/a&gt; is another good open-source way to not only collect metrics but also set up alerts when values exceed certain thresholds that you can set.&lt;/p&gt;
&lt;p&gt;Moving from free and open-source options to more enterprise solutions, &lt;a href="https://www.dynatrace.com/"&gt;Dynatrace&lt;/a&gt; is a powerful tool that allows you to track not just server health but can actually be used to trace individual requests using a custom header.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.appdynamics.com/"&gt;AppDynamics&lt;/a&gt; is another fantastic tool that allows you to really drill down to specific SQL queries that take a long time to execute, for example, feeding you important information to give to your DBAs.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://newrelic.com/"&gt;New Relic&lt;/a&gt; is another good choice for monitoring production as well as test environments, and we particularly love its thread profiling feature.&lt;/p&gt;
&lt;p&gt;Other noteworthy tools are the &lt;a href="https://docs.microsoft.com/en-us/system-center/scom/welcome?view=sc-om-2019"&gt;Microsoft System Center Operations Manager&lt;/a&gt;, the &lt;a href="https://www.oracle.com/technetwork/oem/enterprise-manager/overview/index.html"&gt;Oracle Enterprise Manager&lt;/a&gt;, and &lt;a href="https://support.microsoft.com/en-us/help/3134893/new-with-bluestripe-factfinder-8-1"&gt;BlueStripe FactFinder&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Flood offers out of the box integrations with Dynatrace, AppDynamics and New Relic. If you’re already using one of these tools, you can push the load metrics from Flood into the dashboards that you’re already using, allowing you to see both load metrics and server metrics in the same place. Here’s an example of a dashboard our own Antonio Jimenez created in New Relic which combined Flood-provided metrics with server-side metrics:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20190926-03.png" alt="Combined dashboard displaying Flood’s front end focused metrics alongside back end focused metrics captured by New Relic" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This view helps with spotting correlations or trends across the different sets of data, such as a response time graph that begins to increase at the same time that server memory utilization does.&lt;/p&gt;
&lt;h2 id="putting-it-all-together"&gt;Putting It All Together&lt;/h2&gt;
&lt;p&gt;Having both sides of the puzzle, the metrics from the application servers along with those from our load generators, gives you a more holistic look of your application performance as a whole, and increases the value you get from load testing. We’d encourage you to take advantage of the 5 free node hours we provide in our trial to give this integrated load testing solution a spin. You can head on over to Flood to sign up for a free trial which will include 5 free node hours and you can drop us a note to get help integrating Flood with your APM solution.&lt;/p&gt;</description></item><item><title>Start with Why: How to Write Requirements for API Load Testing</title><link>https://nicolevanderhoeven.com/blog/20190610-start-with-why/</link><pubDate>Wed, 19 Jun 2019 21:49:17 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20190610-start-with-why/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/start-with-why-how-to-write-requirements-for-api-load-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally published &lt;a href="https://www.flood.io/blog/start-with-why-how-to-write-requirements-for-api-load-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Every good test begins with requirements.&lt;/p&gt;
&lt;p&gt;You may think, “But I’m a tester! I’m not a business analyst!”, and I hear you. But as testers, especially on smaller teams or shorter projects, it’s still part of our job to make sure we know what our clients want. Otherwise, how do we know whether we’ve succeeded or failed?&lt;/p&gt;
&lt;p&gt;At Flood, I often speak to project teams who ask for help interpreting reports. Most of those times, the problem is not that they don’t understand the metrics or what they measure. The problem is that they didn’t set pass or fail criteria in the first place, and so they don&amp;rsquo;t have anything to judge the results against. This is always a big warning sign.&lt;/p&gt;
&lt;h2 id="which-performance-testing-metrics-are-important-why-are-we-testing-anyway"&gt;Which performance testing metrics are important? Why are we testing, anyway?&lt;/h2&gt;
&lt;p&gt;Requirements inform every step of the load testing process. Why are we doing load testing? What exactly do we want to test? How will we know when a test has passed or failed? How will we know if application performance is good enough to go into production? What does “good enough” mean?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://flood.io/blog/author/tim-koopmans/"&gt;Tim Koopmans&lt;/a&gt;, co-founder of Flood, coined the acronym SPEAR to describe the different aspects of a performance that should be considered in load testing. This is a great starting point when thinking about what we want our nonfunctional requirements to cover.&lt;/p&gt;
&lt;h3 id="scalability"&gt;Scalability&lt;/h3&gt;
&lt;p&gt;Scalability is the application’s ability to cope with increasing demands by increasing the amount of server resources. This could mean scaling &lt;em&gt;up&lt;/em&gt; (increasing the resources of the dedicated server) or scaling &lt;em&gt;out&lt;/em&gt; (adding more nodes to shoulder the load). What happens when more users than expected sign up in response to a promotion on your site?&lt;/p&gt;
&lt;h3 id="performance"&gt;Performance&lt;/h3&gt;
&lt;p&gt;The most common performance metric is page response time, but there are other considerations to be made here, such as throughput (requests per minute) and the number of concurrent sessions that need to be supported. Things like the total size of the resources on the page, whether or not a CDN is being used, and what to cache are also worth discussing.&lt;/p&gt;
&lt;h3 id="elasticity"&gt;Elasticity&lt;/h3&gt;
&lt;p&gt;Elasticity is a relatively newer aspect to performance testing brought about by advances in the cloud that allow application infrastructure to adapt to changes in load. Unlike scalability, elasticity emphasises scaling down as much as it does scaling up. Testing that virtual machines scale up when load increases is important, but testing that virtual machines also scale down when load decreases can also help save on unnecessary costs.&lt;/p&gt;
&lt;h3 id="availability"&gt;Availability&lt;/h3&gt;
&lt;p&gt;To test for high availability, ask yourself what would happen when (not if) your application’s server fails. Is there another server that the load balancer will seamlessly send traffic towards? Does the throughput fluctuate wildly? If users are connected to one server that fails, is your application smart enough to make new connections to another server? Or will it simply serve up an error page that users won’t know what to do with? Disaster recovery is best tested when there’s no disaster imminent.&lt;/p&gt;
&lt;h3 id="reliability"&gt;Reliability&lt;/h3&gt;
&lt;p&gt;Reliability encompasses a lot of scenarios, but they all have to do with whether or not your application returns expected responses. Does your error rate increase when you increase the duration of your load test? Are you adding verification steps to your load testing scripts to check whether or not the HTTP 200 response that the application returned is not actually an error page?&lt;/p&gt;
&lt;h2 id="what-should-my-applications-response-time-be"&gt;What should my application’s response time be?&lt;/h2&gt;
&lt;p&gt;Our clients frequently ask us what the industry standard is for response times, wanting to make sure their applications measure up. The answer, however, is more complicated than a single number.&lt;/p&gt;
&lt;p&gt;Industry standards for response time are only useful when applications are very similar. Constantly changing technologies used in web development as well as innate differences in business processes, however, make it very difficult to extrapolate a single number that will apply to all, or even most, applications in a certain industry.&lt;/p&gt;
&lt;p&gt;The home page of one e-commerce app, for instance, might be several seconds slower than that of their main competitor. However, that doesn’t take into account the fact that their app loads a video showcasing new products. Does that mean that the development team should remove the video in order to fall in line with their competition?&lt;/p&gt;
&lt;p&gt;Well, maybe. But not necessarily. It’s a business decision that needs to be made after perhaps using focus groups to determine the impact of the video, forecasting changes in conversion rate due to it, and comparing its projected value to the effects of being slower than the competition. A/B experiments could be used to test these assumptions and gather quantifiable data to support the team decision.&lt;/p&gt;
&lt;p&gt;These factors are often not considered in the search for one number to rule them all, which is why a fixation on that number can be detrimental. Instead, I encourage project teams to brainstorm and come up with their own numbers for all metrics that would be more appropriate for their application. Gathering comparative metrics from a competitor may be part of this process.&lt;/p&gt;
&lt;h2 id="what-is-a-good-api-load-testing-requirement"&gt;What is a good API load testing requirement?&lt;/h2&gt;
&lt;p&gt;A good requirement, just like any good goal, is SMART:&lt;/p&gt;
&lt;h3 id="specific"&gt;Specific&lt;/h3&gt;
&lt;p&gt;Vagueness in a requirement leads to vagueness in results interpretation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Instead of&lt;/em&gt;: “The performance of the web application…”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Try&lt;/em&gt;: “The average response time of the Login transaction&amp;hellip;”&lt;/p&gt;
&lt;h3 id="measurable"&gt;Measurable&lt;/h3&gt;
&lt;p&gt;Make sure there is a quantifiable way to know whether requirements have been achieved.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Instead of&lt;/em&gt;: “Decrease user frustration.”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Try&lt;/em&gt;: “The error rate must be below 3% at peak load.”&lt;/p&gt;
&lt;h3 id="agreed-upon"&gt;Agreed Upon&lt;/h3&gt;
&lt;p&gt;Have the appropriate stakeholders been involved?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Instead of&lt;/em&gt;: “The system must be able to generate emails as soon as users register.”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Try&lt;/em&gt;: “The system must be able to generate a maximum of 100 emails an hour, after which emails are queued.”&lt;/p&gt;
&lt;p&gt;A good example for this is a project I was involved in that aimed to increase the speed with which emails were generated. Unfortunately, the particular email being sent included a big change that many customers were expected to contact the support team about. The problem was that nobody had thought to include Support in the conversation. Once they heard about the expected end result, the support team quickly raised their concern that they would not be able to handle the expected volume of emails unless the emails were staggered. This could have been avoided if they had been brought into discussions from the very beginning, in the requirements gathering phase.&lt;/p&gt;
&lt;h3 id="realistic"&gt;Realistic&lt;/h3&gt;
&lt;p&gt;Can we meet this requirement given the resources available?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Instead of&lt;/em&gt;: “All requests must be returned within 5 ms.”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Try&lt;/em&gt;: “90% of the Catalog page requests should be returned within 3 seconds.”&lt;/p&gt;
&lt;h3 id="timely"&gt;Timely&lt;/h3&gt;
&lt;p&gt;Especially for nonfunctional testing, consider adding a timeframe to requirements.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Instead of&lt;/em&gt;: “The digital code is sent by SMS upon successful client log in at peak load.”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Try&lt;/em&gt;: “The digital code will be sent by SMS no later than 5 minutes after successful client log in at peak load.”&lt;/p&gt;
&lt;h2 id="go-forth-and-write-great-load-testing-requirements"&gt;Go forth and write great load testing requirements&lt;/h2&gt;
&lt;p&gt;Requirements represent a great opportunity to think things through and make sure everyone on the project team is on the same page about the goals for your load testing. Too often projects skip this phase, only to realise much later that the tests that were executed didn’t address a key stakeholder’s concerns.&lt;/p&gt;
&lt;p&gt;In load testing, as in many things: when in doubt about what to do, start with why.&lt;/p&gt;</description></item><item><title>Performance Testing Interview Questions, Part 3: LoadRunner</title><link>https://nicolevanderhoeven.com/blog/20181220-performance-testing-interview-questions-part-3/</link><pubDate>Thu, 20 Dec 2018 22:54:24 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20181220-performance-testing-interview-questions-part-3/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/performance-testing-interview-questions-part-3-loadrunner-interview-questions"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/performance-testing-interview-questions-part-3-loadrunner-interview-questions"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Like it or not, &lt;a href="https://www.microfocus.com/en-us/products/loadrunner-load-testing/overview"&gt;LoadRunner&lt;/a&gt; is one of the most popular commercial load test tools, and with good reason. Its protocol support is extensive, and many a load tester has cut his/her teeth on LoadRunner. In this last part of our interview series, we’ll go over some questions you may have to answer when interviewing for a role using Mercury HP MicroFocus LoadRunner.&lt;/p&gt;
&lt;h3 id="what-sort-of-transactions-would-you-put-into-vuser_init-and-vuser_end"&gt;What sort of transactions would you put into vuser_init and vuser_end?&lt;/h3&gt;
&lt;p&gt;Both are used for transactions that would only need to run once, at the beginning and at the end of a script. They’re akin to &lt;a href="http://jmeter.apache.org/"&gt;JMeter&lt;/a&gt;’s &lt;code&gt;setUp&lt;/code&gt; and &lt;code&gt;tearDown&lt;/code&gt; thread groups. Some things you might put in vuser_init are user logins or navigation to get to the start of the business process you really want to test, and logouts and any data cleanup to reset the application for the next test are common things to put in vuser_end. Note that if you want each virtual user to iterate through a list of user accounts during the test, you &lt;em&gt;won’t&lt;/em&gt; want to put logins and logouts in vuser_init and vuser_end.&lt;/p&gt;
&lt;h3 id="when-would-you-run-a-script-in-vugen-and-when-would-you-run-it-in-controller"&gt;When would you run a script in VuGen and when would you run it in Controller?&lt;/h3&gt;
&lt;p&gt;VuGen is short for Virtual User Generator, and it’s where the actual scripting is done. Running in VuGen is good for writing or debugging a script, where you can quickly see where a script fails, what the response is, and what you need to change. You can also step through the code line by line (F10) to more accurately see, for example, variable values changing throughout your script. It’s really only for shaking out scripts with a single user. Running in VuGen also only runs from your local machine. Once you’re ready to run a load test, you’d switch to Controller.&lt;/p&gt;
&lt;p&gt;The Controller is like the command center in LoadRunner: from the Controller, you can actually ramp up the number of users and choose from different scenario configuration settings in order to tailor your workload model to the scenario you’re trying to test. Controller also runs the scripts headlessly; it won’t open up a separate instance of VuGen for every user, so it’s a lot more resource intensive. Controller will also allow you to configure remote load generators and run scripts on them.&lt;/p&gt;
&lt;h3 id="how-would-you-set-up-loadrunner-to-run-certain-transactions-a-certain-percentage-of-the-time-for-example-for-every-100-users-that-browses-to-a-site-you-want-25-of-them-visiting-each-of-the-about-catalog-contact-and-reviews-pages"&gt;How would you set up LoadRunner to run certain transactions a certain percentage of the time? For example, for every 100 users that browses to a site, you want 25 of them visiting each of the About, Catalog, Contact, and Reviews pages.&lt;/h3&gt;
&lt;p&gt;You can write the code to visit each page in separate Actions. Then, in Runtime Settings, you can modify the run logic, create an Action Block that has all four actions, and assign a percentage to them (25%).&lt;/p&gt;
&lt;p&gt;Of course, you can also hard code this in C within the actions, if you’d prefer, but moving these values to Runtime Settings makes it easier to maintain and reuse by other testers.&lt;/p&gt;
&lt;h3 id="how-is-loadrunner-pacing-different-from-how-its-implemented-in-other-tools"&gt;How is LoadRunner pacing different from how it’s implemented in other tools?&lt;/h3&gt;
&lt;p&gt;Most test tools allow you to set a fixed or variable pacing that adds onto however long one iteration takes to complete.&lt;/p&gt;
&lt;p&gt;LoadRunner has a pacing feature that allows you to set a specific time and then adds a variable amount of time to your test &lt;em&gt;depending on response times for the included transactions&lt;/em&gt;. So if you set pacing to 5 minutes and one iteration takes 3 minutes to finish, LoadRunner will automatically wait 2 minutes before starting the next one. If it takes 6 minutes, it will not add any more wait time and proceed instead to the next iteration. This comes in handy because you can more accurately predict how many transactions the script will do in a certain amount of time.&lt;/p&gt;
&lt;h3 id="why-would-the-traffic-from-a-load-test-on-loadrunner-not-be-seen-in-google-analytics"&gt;Why would the traffic from a load test on LoadRunner not be seen in Google Analytics?&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://marketingplatform.google.com/about/analytics/"&gt;Google Analytics&lt;/a&gt; runs when the browser of a user downloads and executes a javascript file that then sends information to Google to increment the site statistics for the current visit. However, most traditional load test tools work on the protocol level; that is, they send requests directly to the application server and download resources from a page to simulate users. This means that while they download the Google Analytics script, they don’t execute it, which means the scripted visits aren’t counted.&lt;/p&gt;
&lt;p&gt;In order to use Google Analytics to view statistics from a load test, you’ll need to use a browser-level load test tool. LoadRunner actually has this covered with their &lt;a href="https://marketplace.microfocus.com/appdelivery/content/truclient"&gt;TruClient&lt;/a&gt; protocol, which interacts with elements on the page like a user would. It will also work for testing single-page applications that use &lt;a href="https://en.wikipedia.org/wiki/Ajax_(programming)"&gt;AJAX&lt;/a&gt; to change the data displayed on a page instead of reloading new pages.&lt;/p&gt;
&lt;p&gt;Of course, for open source alternatives to TruClient, we at Flood prefer &lt;a href="http://element.flood.io/"&gt;Element&lt;/a&gt; or &lt;a href="https://flood.io/blog/load-testing-guide-selenium/"&gt;Selenium&lt;/a&gt; load testing, both of that will actually open the site in a browser and executed scripts like a real user.&lt;/p&gt;</description></item><item><title>Performance Testing Interview Questions, Part 2: JMeter</title><link>https://nicolevanderhoeven.com/blog/20181220-performance-testing-interview-questions-part-2/</link><pubDate>Thu, 20 Dec 2018 14:47:18 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20181220-performance-testing-interview-questions-part-2/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/performance-testing-interview-questions-part-2-jmeter-interview-questions"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/performance-testing-interview-questions-part-2-jmeter-interview-questions"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this part, we’ll go over some questions that are commonly asked in technical interviews about the use of the most popular open-source load testing tool, &lt;a href="http://jmeter.apache.org/"&gt;Apache JMeter&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="using-jmeter-you-record-a-key-business-process-upon-playing-it-back-however-you-notice-an-error-on-the-first-page-what-could-have-gone-wrong-and-how-would-you-fix-this"&gt;Using JMeter, you record a key business process. Upon playing it back, however, you notice an error on the first page. What could have gone wrong and how would you fix this?&lt;/h3&gt;
&lt;p&gt;While recorders have gone a long way towards making scripting easier, modern security measures like the implementation of viewstates have also made scripting more complicated to record. Some more complicated applications will pass dynamic values in responses that it expects to see in the succeeding request, otherwise the request is blocked. This is great for security, but it also means that you will need to manually extract these values in your script.&lt;/p&gt;
&lt;p&gt;In JMeter, you would do this by adding a response extractor to your sample that will extract the dynamic string according to rules you set, and then save the string to a parameter. This parameter must then be used in the next request that you send.&lt;/p&gt;
&lt;p&gt;While running your test plan, you get the error: &lt;code&gt;java.lang.OutOfMemoryError: Java heap space&lt;/code&gt;. How do you fix it?
The easy fix is to edit &lt;code&gt;jmeter.bat&lt;/code&gt; and increase the heap memory (search for &lt;code&gt;HEAP&lt;/code&gt;) and increase heap space allocated like this:
&lt;code&gt;HEAP=&amp;quot;-Xms512m -Xmx2048m&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The part in bold increases the heap space to 2 GB.&lt;/p&gt;
&lt;p&gt;However, it’s also worthwhile looking at your test plan and removing any obvious memory hogs. One of the things we see a lot is enabled listeners that aren’t even being used. Listeners are great while debugging, but should be disabled when running a load test. Writing too many lines to the log is another culprit here that should also be commented out when running important tests. And of course, you should always run JMeter in non-GUI mode during load tests!&lt;/p&gt;
&lt;h3 id="after-running-a-load-test-the-results-show-that-300-transactions-were-executed-with-a-passed-status-and-returning-responses-of-http-200-however-the-dba-tells-you-that-there-were-no-records-written-to-the-database-as-a-result-of-the-transactions-how-would-you-troubleshoot-this"&gt;After running a load test, the results show that 300 transactions were executed with a PASSED status and returning responses of HTTP 200. However, the DBA tells you that there were no records written to the database as a result of the transactions. How would you troubleshoot this?&lt;/h3&gt;
&lt;p&gt;I see this happening with clients all the time, so I think this makes a great technical interview question. This is also why I said earlier that you shouldn’t count on a test tool to tell you a test has passed. It’s possible for a transaction to have the status PASSED and return an HTTP 200 but still be hiding an error.&lt;/p&gt;
&lt;p&gt;This is mainly due to poor scripting that does not include validations that check whether the script has gotten to the right page. For example, you could use a Response Assertion as a child of your home page sample with the words “Welcome, Nicole” to determine whether the user Nicole has logged in successfully.&lt;/p&gt;
&lt;p&gt;This is a critical step, because without it, it’s possible for a server to return a nice error page, or an incorrect one, such as the home page, with an HTTP 200.&lt;/p&gt;
&lt;h3 id="how-do-you-use-a-data-file-to-read-user-account-details-from-a-csv-in-jmeter"&gt;How do you use a data file to read user account details from a CSV in JMeter?&lt;/h3&gt;
&lt;p&gt;JMeter has a &lt;a href="https://jmeter.apache.org/usermanual/component_reference.html#CSV_Data_Set_Config"&gt;CSV Data Set Config Element&lt;/a&gt; built specifically for this purpose. You can simply add one to your test plan, browse to the CSV file that you’d like to use, and select the options for how to share that data among the threads in your JMeter script.&lt;/p&gt;
&lt;h3 id="how-would-you-go-about-testing-a-website-that-uses-http2-on-jmeter"&gt;How would you go about testing a website that uses HTTP/2 on JMeter?&lt;/h3&gt;
&lt;p&gt;There is a JMeter plugin called HTTP/2 Sampler that accomplishes just this. My colleague Jason just wrote &lt;a href="https://flood.io/blog/jmeter-tutorial-http2-test/"&gt;a blog post&lt;/a&gt; on this very topic, but basically the plugin adds new HTTP/2 versions to the standard HTTP request and request defaults samplers as well as the Simple Data Writer and View Results Tree listener. These elements will need to be used in order to test an application using HTTP/2.&lt;/p&gt;
&lt;h3 id="what-can-you-do-to-synchronise-the-execution-of-multiple-jmeter-instances-on-different-load-generators"&gt;What can you do to synchronise the execution of multiple JMeter instances on different load generators?&lt;/h3&gt;
&lt;p&gt;Apache JMeter has released &lt;a href="https://jmeter.apache.org/usermanual/jmeter_distributed_testing_step_by_step.pdf"&gt;this handy PDF file&lt;/a&gt; to describe the steps necessary to run JMeter in distributed mode. This involves installing Java and JMeter on all machines and setting them up in a master and slave configuration so that one machine controls the rest. On the master, you’ll need to add remote hosts to the jmeter.properties file. When everything is set up, you should be able to open up JMeter on the master and do a a remote start of your current test plan on any or all slaves that you choose.&lt;/p&gt;
&lt;p&gt;The problem with this setup is the clunkiness of working with different machines and essentially having to set them up the same way. After the test, there’s the daunting task of logging back onto each machine, gathering the data, and transferring them back into a central repository. Then you’re faced with consolidating the data from each injector in order to see a picture of the whole test. You can see that this set up quickly goes from time-consuming to a nightmare, especially when potentially thousands of different machines are involved.&lt;/p&gt;
&lt;p&gt;If only there were &lt;a href="https://flood.io/"&gt;an easier way&lt;/a&gt;&amp;hellip;&lt;/p&gt;</description></item><item><title>Performance Testing Interview Questions, Part 1: General</title><link>https://nicolevanderhoeven.com/blog/20181220-performance-testing-interview-questions-part-1-general/</link><pubDate>Thu, 20 Dec 2018 12:41:40 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20181220-performance-testing-interview-questions-part-1-general/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/performance-testing-interview-questions-part-1-general"&gt;
&lt;p&gt;&lt;em&gt;Originally published &lt;a href="https://www.flood.io/blog/performance-testing-interview-questions-part-1-general"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="preparation-is-key-for-the-perfect-interview"&gt;Preparation is Key for the Perfect Interview&lt;/h2&gt;
&lt;p&gt;Performance testing interviews can be a nerve wracking experience. We all understand it’s necessary, but we dread it anyway. Each company has its own style of interview, so you may need to prepare differently for each opportunity. This interview series will cover some general sample questions and scenarios as well as my answers to them to help you nail that performance testing interview!&lt;/p&gt;
&lt;p&gt;I’ve broken down the potential questions into the most popular categories, so you can prepare easily for your all-important interview:&lt;/p&gt;
&lt;h3 id="what-is-performance-testing--what-is-load-testing--what-is-stress-testing"&gt;What is Performance Testing? What is Load testing? What is Stress Testing?&lt;/h3&gt;
&lt;p&gt;Performance testing is the process of systematically determining, quantifying and improving the speed, reliability, and efficiency of an application with the aim of improving user experience. This includes both front-end and back-end performance testing.&lt;/p&gt;
&lt;p&gt;Load testing is a specific type of back-end performance testing where automated scripts are written to send requests in an attempt to simulate the peak user load expected on the application servers.&lt;/p&gt;
&lt;p&gt;Stress testing takes that one step further and applies load in excess of what is expected in order to determine the maximum capacity of the system under test.&lt;/p&gt;
&lt;h3 id="what-is-the-difference-between-front-end-and-back-end-performance-testing-which-one-is-more-important"&gt;What is the difference between front-end and back-end performance testing? Which one is more important?&lt;/h3&gt;
&lt;p&gt;Both front-end and back-end performance testing measure how fast an application responds, but they measure different components of that overall user response time.&lt;/p&gt;
&lt;p&gt;Front-end performance is concerned with how quickly text, images, and other page elements are displayed on a user’s browser page. Back-end performance is concerned with how quickly these elements are processed by the site’s servers and sent to the user’s machine upon request. Front-end performance is the part of the iceberg above the water line, and back-end performance is everything underneath that you can’t see.&lt;/p&gt;
&lt;p&gt;Both are important because they can both determine whether a user continues to use your application. Front-end performance tends to be easier to test and can provide some quick wins due to the large amount of optimisation tweaks that can be done without writing code. Back-end performance tends to be more difficult to test because it often uncovers problems with the underlying infrastructure and hardware that are of a more technical nature.&lt;/p&gt;
&lt;h3 id="why-does-performance-testing-matter"&gt;Why does performance testing matter?&lt;/h3&gt;
&lt;p&gt;Performance testing matters because application performance has a significant impact on user experience. A site that is unreachable or slow to load due to an inability to cope with unexpected load will cause users to browse to competitor’s sites and tarnish the brand’s reputation.&lt;/p&gt;
&lt;h3 id="how-do-you-know-when-a-load-test-has-passed"&gt;How do you know when a load test has passed?&lt;/h3&gt;
&lt;p&gt;Ideally, you would have discussed your nonfunctional requirements with key stakeholders before load testing begins. This means that you set your own pass criteria before you even run the tests. You would ideally have a list of specific transactions (selected based on criticality or complexity according to the business) whose response time needs to fall under a threshold you’ve predetermined. “Fast” is not specific enough&amp;ndash; a number is better. Depending on what kind of tests you’re running (soak, stress, volume, etc) you may have other nonfunctional requirements about duration, resource utilisation on the server side, or specific outcomes to scenarios you’d like to test.&lt;/p&gt;
&lt;p&gt;As a general rule, don’t rely on a load test tool to determine whether your load test has passed. Rely on it to report your results, but always compare the results to the requirements to determine successes or failures.&lt;/p&gt;
&lt;h3 id="what-would-you-advise-to-clients-who-say-they-cant-afford-to-performance-test-because-they-dont-have-the-resources-to-maintain-several-load-generators-on-site"&gt;What would you advise to clients who say they can’t afford to performance test because they don’t have the resources to maintain several load generators on site?&lt;/h3&gt;
&lt;p&gt;This is the main reason that performance testing has for so long been considered a luxury that only big companies can afford. Luckily technology moves on, and in 2018 we’re at a point where everyone can load test. The big innovation here has been the cloud and the ability to spin up thousands of virtual machines with a few mouse clicks. Services like Amazon AWS, Microsoft Azure and Google Cloud make it so that every budding entrepreneur can “borrow” the computing hardware necessary to do cloud load testing with thousands of users and then give them back after the test, without the hassle and cost of maintaining them. I would advise the clients to look for a a cloud load testing solution that utilizes virtual machines on the cloud to run their tests affordably.&lt;/p&gt;
&lt;h3 id="you-run-a-load-test-against-a-server-with-4gb-ram-and-the-results-show-an-average-response-time-of-30-seconds-for-a-particular-request-the-production-server-has-been-allocated-8gb-ram-what-would-you-expect-the-average-response-time-of-the-same-request-to-be-in-production-given-the-same-load"&gt;You run a load test against a server with 4GB RAM and the results show an average response time of 30 seconds for a particular request. The production server has been allocated 8GB RAM. What would you expect the average response time of the same request to be in production given the same load?&lt;/h3&gt;
&lt;p&gt;Trick question! While you may be tempted to answer that the response time would be halved to 15 seconds, reality is rarely that convenient. Response times are a factor of so much more than memory. Things like CPU utilisation, network throughput, latency, load balancing configuration, and just application logic are always going to influence load tests. You can’t assume a linear progression in response time just because you’ve upgraded one part of the hardware. This is why it’s important to load test against an environment that is as production-like as possible.&lt;/p&gt;
&lt;h3 id="what-is-a-percentile-and-why-would-you-look-at-percentile-response-times-when-you-already-have-average-response-times"&gt;What is a percentile and why would you look at percentile response times when you already have average response times?&lt;/h3&gt;
&lt;p&gt;A percentile is a statistical measure that describes a value that a certain percentage of the sample either meets or falls under. For example, a 90th percentile response time of 5 seconds means that 90% of the responses took 5 seconds or less to be returned. It can be an important measure because they soften the impact that outliers have on more inclusive measures such as averages. A transaction with an average response time of 2.5 seconds may seem perfectly acceptable to the business, but when the 90th percentile response time is 20 seconds, this is a good reason to investigate further.&lt;/p&gt;
&lt;h3 id="what-are-some-trends-in-performance-testing-that-you-think-will-continue-in-2019-and-beyond"&gt;What are some trends in performance testing that you think will continue in 2019 and beyond?&lt;/h3&gt;
&lt;p&gt;Cloud is an easy answer, as the cloud brings some compelling benefits in terms of reduced cost and just ease of use. However, I already touched on that in a previous question, so I’ll talk about another trend: open source.&lt;/p&gt;
&lt;p&gt;There’s a reason that open source is still around: it works. The main advantage of open source tools is not that they are free, although that is a big part of the appeal. The real advantage is that open source tools are community-based and community-led, which means features get built for it faster than sometimes commercial tools can keep up with, and they’re built by users of the tool themselves. Open source tools like &lt;a href="http://jmeter.apache.org/"&gt;JMeter&lt;/a&gt;, &lt;a href="http://gatling.io/"&gt;Gatling&lt;/a&gt; and &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt; have revolutionized the industry due to their impressive feature sets, built by a growing community that has developed plugins for everything you can think of. More and more, even big companies with big budgets choose to go open source simply because of the wealth of knowledge of these tools that are already available for free.&lt;/p&gt;
&lt;p&gt;We’re huge proponents of open source tools at Flood, which is why we recently open-sourced our very own tool, &lt;a href="https://element.flood.io/"&gt;Element&lt;/a&gt;. We will continue to support all things open source, because we firmly believe that the future is open source.&lt;/p&gt;</description></item><item><title>Tips and Tricks for Using Flood Element</title><link>https://nicolevanderhoeven.com/blog/20181123-tips-and-tricks-for-using-flood-element/</link><pubDate>Fri, 23 Nov 2018 23:07:24 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20181123-tips-and-tricks-for-using-flood-element/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/tips-and-tricks-for-using-flood-element"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/tips-and-tricks-for-using-flood-element"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In case you missed it, there’s a new performance test tool on the block, and it’s called &lt;a href="https://element.flood.io/"&gt;Flood Element&lt;/a&gt;. It’s a scalable browser-based tool that allows you to write scripts in javascript that interact with web pages like a real user.&lt;/p&gt;
&lt;p&gt;Using Browser Level Users is a &lt;a href="https://flood.io/blog/why-you-should-load-test-with-browsers/"&gt;newer approach to load testing&lt;/a&gt; that overcomes a lot of the common challenges we hear about traditional methods of testing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Easier scripting that is akin to common functional tools like Selenium and easier to learn&lt;/li&gt;
&lt;li&gt;More realistic results that are based on true browser performance rather than API responses&lt;/li&gt;
&lt;li&gt;Ability to test against all components of your web app, including things like Javascript that is rendered via the browser&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Given the above benefits, it’s a no brainer to check out Flood Element for your web load testing, especially if you have struggled with the existing tools on the market like JMeter or HP LoadRunner.&lt;/p&gt;
&lt;p&gt;Pairing Element with &lt;a href="https://flood.io/"&gt;Flood&lt;/a&gt; turns it into a pretty powerful load test tool. We have a &lt;a href="https://help.flood.io/getting-started-with-load-testing/step-by-step-guide-flood-element"&gt;great guide here&lt;/a&gt; that you can follow if you’d like to get started with it. As you can imagine, I’ve been using and testing Element for several months now and I thought I’d share some tips I’ve learned along the way.&lt;/p&gt;
&lt;h2 id="initialising-your-script"&gt;Initialising your script&lt;/h2&gt;
&lt;p&gt;You can always start from scratch, but the quickest way to get started is to type&lt;/p&gt;
&lt;p&gt;&lt;code&gt;element init myfirstelementtest&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;from your terminal, filling in the project name you’d prefer.&lt;/p&gt;
&lt;p&gt;You’ll then be asked to type in the title of your test as well as the URL you’d like to script against. After a minute, you’ll see that a new directory has been created:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-01.png" alt="New directory in Flood Element" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Element will automatically create a file called &lt;code&gt;test.ts&lt;/code&gt;. This file will contain the skeleton of a script, along with some sample code to help you find a button and then click on it. But before you open it, let’s move on to…&lt;/p&gt;
&lt;h2 id="choosing-the-right-text-editor"&gt;Choosing the right text editor&lt;/h2&gt;
&lt;p&gt;Scripting in Element is already pretty simple, but I’ve found two things that really help while scripting are syntax highlighting and code completion. Syntax highlighting will greatly improve your experience when first learning a new test tool like Element, and code completion will make your scripting lightning fast as you become more experienced in the process. My text editor of choice is &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt;, which has both of those features. It’s slick and clean, and it does the job.&lt;/p&gt;
&lt;p&gt;Syntax highlighting is when the text editor intelligently changes the font colour of your code according to its role in the programming language you’re using. Here’s a screenshot of the test.ts file we generated earlier in VS Code to show you what I mean:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-02.png" alt="Generated sample Flood Element script" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This makes it easier to make sense of the code at a glance: comments are in green, values and labels are in orange, etc.&lt;/p&gt;
&lt;p&gt;Code completion is when you start to type something, and VS Code helpfully opens a context menu with suggestions for methods you can use.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-03.gif" alt="Auto-completion in VSCode" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I love this because it means I don’t have to remember the exact name of the method. It also suggests names of variables you’ve already defined and highlights code that doesn’t make sense. This will help to make your tests more maintainable and readable for others, which is a great benefit as you look to scale your testing out in the future.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-04.gif" alt="Suggestions in VSCode" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="taking-screenshots"&gt;Taking screenshots&lt;/h2&gt;
&lt;p&gt;One of the most powerful features of Element is its ability to take screenshots. I find it immensely useful when debugging because sometimes it’s just easier to see what’s going on visually. With protocol based tools, debugging can often be a much more involved and technical process.&lt;/p&gt;
&lt;p&gt;There are two ways to take screenshots in Element:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a setting to automatically take a screenshot when an error is encountered. You can do this by setting screenshotOnFailure to true in TestSettings:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;device&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Device&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;iPadLandscape&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;userAgent&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;flood-chrome-test&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;clearCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;disableCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;screenshotOnFailure&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;Explicitly take a screenshot at a particular point in the script. You can do this by adding&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to your code.&lt;/p&gt;
&lt;h2 id="viewing-screenshots"&gt;Viewing Screenshots&lt;/h2&gt;
&lt;p&gt;Once you’ve taken screenshots within your tests, you will likely want to view them and know that they will be stored for future safekeeping. Whether you are running your test locally on have uploaded it to Flood to run with increased concurrency, Flood Element has you covered.&lt;/p&gt;
&lt;h3 id="locally-run-tests"&gt;Locally Run Tests&lt;/h3&gt;
&lt;p&gt;Screenshots will be saved as jpg files in a timestamped folder corresponding to your run. It should look something like: &lt;code&gt;...myfirstelementtest/tmp/element-results/test/2018-11-20T135700.595Z/flood/screenshots/&lt;/code&gt; and the screenshots will be uniquely named so that new screenshots, even for the same step, don’t overwrite older ones.&lt;/p&gt;
&lt;p&gt;However, I rarely have to look up the screenshots in that folder because I prefer to see them in iTerm2 for macOS. iTerm is an alternative to the terminal that works particularly well with Element. When you take a screenshot, iTerm actually shows it in-line:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-05.png" alt="Screenshots shown inline with iTerm" loading="lazy" decoding="async"&gt;
‍&lt;/p&gt;
&lt;h3 id="tests-run-in-flood"&gt;Tests Run in Flood&lt;/h3&gt;
&lt;p&gt;Running an Element script on Flood is ideal when you need larger concurrency. Rather than accessing your screenshot locally, Flood will centralize the images into your account so the images remain even after the cloud load injectors are destroyed. You can get to the screenshot files by downloading Archived Results:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-06.png" alt="Archived Results in Flood" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Or you can also choose to click on a step on the dashboard to see a film strip of your test:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-07.png" alt="Seeing screenshots during a test with Flood" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="using-logs"&gt;Using Logs&lt;/h2&gt;
&lt;p&gt;You may need to check out the logs for more technical debugging, especially when the screenshots don’t tell the whole story. Whether you are running your test locally on have uploaded it to Flood to run with increased concurrency, Flood Element has you covered.&lt;/p&gt;
&lt;h3 id="locally-run-tests-1"&gt;Locally Run Tests&lt;/h3&gt;
&lt;p&gt;You can print to the console by typing, for example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;console.log('orderValues = ' + orderValues)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will print the value of the variable orderValues at that point in the script. You would see this in your terminal if you’re running Element locally.&lt;/p&gt;
&lt;h3 id="tests-run-in-flood-1"&gt;Tests Run in Flood&lt;/h3&gt;
&lt;p&gt;If you’re running the script on Flood, you can either download the log (in the same Archived Results zipped file as mentioned earlier) or you can click on the Logs tab:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181123-08.png" alt="Test logs on Flood" loading="lazy" decoding="async"&gt;
‍&lt;/p&gt;
&lt;h2 id="fun-with-flags"&gt;Fun with Flags&lt;/h2&gt;
&lt;p&gt;Element comes with a few flags that give you more control over how the script is run locally. Here are a few of my favourites!&lt;/p&gt;
&lt;h3 id="headless-flag"&gt;Headless Flag&lt;/h3&gt;
&lt;p&gt;When in doubt, run Element in non-headless mode to see the script actually opening the web app on Chrome and interacting with the page. This is only possible locally, but there’s nothing like actually seeing for yourself what’s happening in real time instead of relying on screenshots and logs after the fact. To enable this mode, add the flag when running your test:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;element run myfirstelementtest.ts --no-headless&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="watch-flag"&gt;Watch Flag&lt;/h3&gt;
&lt;p&gt;Element will automatically close the browser window when it encounters an error or finishes the iteration. Adding &lt;code&gt;--watch&lt;/code&gt; will leave the browser window open and then will monitor the script. As soon as the script is saved, it will automatically run it in the same window from the beginning. Simply add this flag like the above example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;element run myfirstelement.ts --watch&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="dev-tools-flag"&gt;Dev Tools Flag&lt;/h3&gt;
&lt;p&gt;This opens a browser instance and runs the script with the Chrome Dev Tools open, allowing you to find locators for the next action you want to script. Simply add this flag like the first example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--dev-tools&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="more-flags"&gt;More Flags&lt;/h3&gt;
&lt;p&gt;For more flags, use:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;element run --help&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="try-element"&gt;Try Element&lt;/h2&gt;
&lt;p&gt;You’ve just gotten a crash course on Flood Element. You’re all set! If you haven’t already, download Element &lt;a href="https://element.flood.io/"&gt;here&lt;/a&gt; to start writing functional test scripts and reusing them as load test scripts on Flood. If you don’t have a Flood account, you can easily sign up for a free trial here.&lt;/p&gt;
&lt;p&gt;We’re extremely proud to have been able to contribute to the open-source community and can’t wait to have you try this new addition to the Flood line!&lt;/p&gt;</description></item><item><title>Prepare for Viral Videos With Flood Element</title><link>https://nicolevanderhoeven.com/blog/20181108-prepare-for-viral-videos-with-flood-element/</link><pubDate>Thu, 08 Nov 2018 23:24:31 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20181108-prepare-for-viral-videos-with-flood-element/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/prepare-for-viral-videos-with-flood-element"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/prepare-for-viral-videos-with-flood-element"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One of the more interesting things to load test is video. You see, traditional load test tools are protocol-based, which means that their approach to load testing video is to capture and simulate the traffic that the browser generates under the covers, sending the same requests to the application. This approach can still be successful&amp;ndash; in fact, one of our favourite clients here at Flood recently &lt;a href="https://blog.hotstar.com/millons-scale-simulations-1602befe1ce5"&gt;used Gatling to simulate 6 million concurrent users&lt;/a&gt; streaming video from their application. However, for some use cases, testing at the protocol level can’t simulate everything that happens in a real world use case.&lt;/p&gt;
&lt;h2 id="the-challenge"&gt;The Challenge&lt;/h2&gt;
&lt;p&gt;If you’re a video creator, the ultimate goal is often a simple goal: creating a viral video. Unfortunately, preparing for and simulating this all-important use case is typically impossible with protocol based tools. The same feature that makes them so efficient &amp;ndash; not using a browser to send the requests &amp;ndash; is the same thing that prevents them from being used to simulate the needed browser level events. You see, things like view counts on a video are normally tracked by scripts running on the page. Traditional tools download these scripts along with other page resources, but the scripts themselves are not executed. Enter Flood Element.&lt;/p&gt;
&lt;h2 id="a-solution-flood-element"&gt;A Solution: Flood Element&lt;/h2&gt;
&lt;p&gt;Flood Element represents a new way of looking at load testing. It is a browser-based tool, which means that it actually drives an instance of the browser to test an application. Rather than telling it what requests to send to simulate a user like you would do with a protocol-based tool, you tell Element which buttons to click or which elements on a page to interact with just as a real user would. Essentially Element IS a user, using your application the way it was meant to be used. This means that it also executes any scripts that would run on the browser side. It’s so realistic that you could even use Element to make your YouTube video viral* by scripting up users to view your content… hypothetically speaking.&lt;/p&gt;
&lt;h2 id="simulating-real-user-views"&gt;Simulating Real User Views&lt;/h2&gt;
&lt;p&gt;Testing video with Flood Element is actually pretty easy, especially if you are familiar with open source testing tools like Selenium. If you haven’t yet begun using Element, go through the installation guide &lt;a href="https://element.flood.io/"&gt;here&lt;/a&gt;. You can download the full script used for this tutorial &lt;a href="https://github.com/flood-io/element/blob/master/examples/youtube.ts"&gt;here&lt;/a&gt;. In this example, we’re going to be using a YouTube video of the opening credits the classical cartoon Top Cat (the indisputable leader of the gang) from Warner Bros.&lt;/p&gt;
&lt;h3 id="step-1-navigate-to-the-video-page"&gt;Step 1. Navigate to the video page&lt;/h3&gt;
&lt;p&gt;Let’s start with a basic script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;@flood/element&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;settings&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loopCount&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clearCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;disableCache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;actionDelay&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;stepDelay&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;screenshotOnFailure&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;userAgent&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;flood-element-test&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;01_Home&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;visit&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;https://www.youtube.com/watch?v=6fvhLlrBrPQI&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the bones of a basic script in Flood Element. It consists of a single step that involves doing two things: visiting the starting URL, which in this case is the direct link to the YouTube video, and then taking a screenshot once loading the page. Taking screenshots is particularly useful when testing video because screenshots give you a way to check that your script is doing what you want it to do. It’s good to take periodic screenshots as well, to measure progress.&lt;/p&gt;
&lt;p&gt;To help you validate that the script runs well locally before scaling to hundreds or thousands of users, you can use our Element CLI. If you save the above code as youtube.ts, open up your terminal in that directory and then type:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;element run youtube.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Assuming you’ve got Element set up correctly, you should see something like this: (Note that screenshots are only available when using iTerm as your shell. They are also saved as files under &lt;code&gt;.../tmp/element-results/youtube&lt;/code&gt; .)
‍
&lt;img src="https://nicolevanderhoeven.com/assets/20181108-01.png" alt="Screenshots shown while running Element in iTerm" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Oops! It looks like our script does navigate to the page, but it doesn’t play the video. We’ll need to click on that play button. Let’s add that to our script.&lt;/p&gt;
&lt;h3 id="step-2-click-the-play-button"&gt;Step 2. Click the Play button&lt;/h3&gt;
&lt;p&gt;We’ll need to add some lines to click the play button after visiting the page but before we take the screenshot, so that we can verify later whether or not the play button was clicked.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;02_ClickPlay&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Click on Play button
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;playBtn&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;findElement&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;xpath&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;//button[@aria-label=&amp;#34;Play&amp;#34;]&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;playBtn&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;click&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example I used Xpath, but you do have the option to select based on partial text, CSS selector, etc. A tool I like to use to get these is Chrome Developer Tools’ Elements tab, which allows you to get the selectors for a particular element.&lt;/p&gt;
&lt;h3 id="step-3-take-periodic-screenshots"&gt;Step 3. Take periodic screenshots&lt;/h3&gt;
&lt;p&gt;To make sure that the video is really playing, we’ll need to take a screenshot a few seconds after the initial screenshot in order to visually confirm that the video has progressed. Even better, we can get Element to take screenshots at regular intervals. So let’s add a third step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;03_Play&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt; =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//Take a screenshot every 5 seconds until video finishes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;‍&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;60&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;takeScreenshot&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;browser&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;wait&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To use &lt;code&gt;i&lt;/code&gt;, we&amp;rsquo;ll also need to declare it earlier in the script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;step&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;TestSettings&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;By&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Until&lt;/span&gt; } &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;@flood/element&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;‍&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this step, &lt;code&gt;i&lt;/code&gt; will correspond to every 5 seconds of playtime for the video, so we&amp;rsquo;ll need to make sure that the limit of &lt;code&gt;i&lt;/code&gt; that we set is sufficient time for the entire video to play. Every 5 seconds, Element will take a screenshot, which will help us verify that the video is progressing after the test.&lt;/p&gt;
&lt;p&gt;Click &lt;a href="https://github.com/flood-io/element/blob/master/examples/youtube.ts"&gt;here&lt;/a&gt; to download the script before moving on to the next step, unless you’ve been following along at home.&lt;/p&gt;
&lt;h3 id="step-4-run-it-on-flood"&gt;Step 4. Run it on Flood!&lt;/h3&gt;
&lt;p&gt;The final step is to upload it to Flood and run the test. Click &lt;a href="https://guides.flood.io/scripting-and-tools/flood-element/getting-started-with-element"&gt;here&lt;/a&gt; to see a step-by-step guide for how to do just that. You can choose how many users you’d like to run on Flood and across how many nodes. New users will be given 5 node hours which will allow them to run a test with up to 250 users for 1 hour. Each node will have a different IP address, so plan for that accordingly if your application takes that into account.&lt;/p&gt;
&lt;p&gt;One of the things I love about running it this way is that if you click on the arrow to the right of the sole transaction, you can see screenshots taken along the way to prove that the video is being played:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20181108-02.jpg" alt="Screenshots during test execution in Flood" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="some-considerations-for-load-testing-video-with-flood-element"&gt;Some Considerations for Load Testing Video with Flood Element&lt;/h2&gt;
&lt;h3 id="taking-screenshots-and-using-logs-are-necessary-for-debugging"&gt;Taking screenshots and using logs are necessary for debugging.&lt;/h3&gt;
&lt;p&gt;Because the page elements while a video is streaming are static, we’re not going to be able to use those as clues as to where we are in our scenario. To compensate, it’s a good idea to take screenshots and use console.log() to determine progress, especially while debugging. Be careful not to go overboard on these when actually running the load test though, as they will consume system resources.&lt;/p&gt;
&lt;h3 id="videos-may-behave-differently-in-headless-mode"&gt;Videos may behave differently in headless mode.&lt;/h3&gt;
&lt;p&gt;While writing this tutorial, I realised that YouTube auto-plays videos when on non-headless mode (aka, when opening a video link manually on a browser or with the &lt;code&gt;--no-headless&lt;/code&gt; flag in Element). When it’s executed in headless mode via Flood Element, it requires a click on the play button to begin. Again, use screenshots to aid you in spotting these differences.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Element’s browser-level scripting capabilities allow it to simulate users that interact with pages just like human users would. Element and Flood are a powerhouse combo, providing the flexibility to test with thousands of users across the world within a few minutes.&lt;/p&gt;
&lt;p&gt;Element seeks to reduce the gap between a scripted user and a real user &amp;ndash; and it’s so realistic that it’s hard to tell the difference. We encourage you to take advantage of our free trial offer and run some real world load tests today!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;*We don’t advise or condone the use of Element to artificially inflate views on YouTube in any way which may violate its &lt;a href="https://www.youtube.com/static?template=terms"&gt;Terms of Service&lt;/a&gt;. The purpose of this article is to showcase Element and how realistically it recreates real users.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Mitad de allá y mitad de aquí: reflexiones de una políglota multinacional</title><link>https://nicolevanderhoeven.com/blog/20180828-mitad-de-alla/</link><pubDate>Tue, 28 Aug 2018 23:19:32 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20180828-mitad-de-alla/</guid><description>&lt;p&gt;Originally posted on &lt;a href="https://medium.com/@n_vanderhoeven/mitad-de-all%C3%A1-y-mitad-de-aqu%C3%AD-reflexiones-de-una-pol%C3%ADglota-multinacional-1d192f488738"&gt;Medium&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nadie se da cuenta de que soy filipina. Incluso los filipinos desconocidos me miran e inmediatamente hablan en inglés. Siempre están sorprendidos cuando respondo en Tagalog. «¿Pero por qué tienes la piel clara?» me preguntan, como si fuera algo que se puede comprar en una tienda. A través de mi piel no pueden ver mi lugar de nacimiento y juventud en las Filipinas, o cuánto echo de menos la voz del vendedor de taho cuando caminas por las calles: «Tahoooooooo!»&lt;/p&gt;
&lt;p&gt;Los chinos me miran siempre como si fuera una de ellos. Pero después de hablar unos segundos en chino conmigo retroceden con una sonrisa. «Disculpa, es que pensábamos que eras china.» No les importa que tengo sangre china. Lo que les importa es que mi chino es horrible.&lt;/p&gt;
&lt;p&gt;En EE.UU piensan que soy americana por mi acento en inglés por teléfono, pero cuando me ven, dicen «¡Que bueno tu inglés! ¿Qué eres?» Una mujer? Una ingeniera de software? No — un secreto a voces: &lt;em&gt;No eres una de nosotros&lt;/em&gt;. Eso después del hecho de que todos mis estudios han sido en inglés, y que es mi mejor lengua.&lt;/p&gt;
&lt;p&gt;En una entrevista de trabajo en Australia, me preguntó el entrevistador de todas las maneras posibles de dónde vengo, y, frustrado con mis respuestas sobre mis trabajos previos en Australia, se alegró mucho al descubrir que mis padres eran filipinos. «¡Todavía no tenemos un filipino aquí!» me dijo. Después, la oferta, y mi vergüenza de conseguir un trabajo por no ser australiana. Yo con una carrera casi completamente hecha en el país.&lt;/p&gt;
&lt;p&gt;Un holandés me dijo que hay tanta tolerancia en los países bajos. Todas las personas pueden convertirse en holandeses independientemente de sus orígenes. Pero justo cuando explico cómo es posible tener tres ciudadanías, empieza a sacudir la cabeza. «Pero eso no es justo. ¡Tu tienes que elegir! O no serás una verdadera holandesa.» Eso después de un día hablando en holandés.&lt;/p&gt;
&lt;p&gt;Puedes hacerme otras preguntas, las que van a hacerte sentir más seguro de quién soy, o más capaz de etiquetarme. Y ofrezco mis respuestas, una mezcla y combinación de países. Pero lo que quiero hacer es contar mi vida, porque no puedo contestar en una sola palabra: Soy originalmente filipina. Mis padres son filipinos. Estudiaba en inglés en una escuela internacional, y más tarde en EE.UU. Me mudé a Australia y me casé allí con mi marido australiano. Vivimos allí juntos por más de una década. Nos mudamos a los Países Bajos, donde vivimos ahora. Y todo eso con una colección de idiomas, algunos de esos países, y otros, como el español, de otros.&lt;/p&gt;
&lt;p&gt;Tengo mucha pasión cuando hablo otros idiomas. Y el aprendizaje de un idioma no es posible sin el conocimiento y la comprensión de la cultura que acompaña cada idioma. ¿En qué idioma pienso? Eso depende — de la situación, de la persona con la que estoy hablando, de mi humor, del tema, de si estoy cansada. Pero pienso en todos. Y, cuando pienso en estos idiomas, también actúo de manera correspondiente a las perspectivas inherentes a ellos. Y me cambian. Soy fundamentalmente diferente por haber aprendido este particular conjunto de idiomas.&lt;/p&gt;
&lt;p&gt;Me vas a preguntar a qué país pertenezco en mi corazón?&lt;/p&gt;
&lt;p&gt;Elegir un país o una cultura sería como cortarme el brazo. ¿Cómo sería posible para mí vivir sin reconocer la importancia de la familia como una filipina, sin cambiar al inglés para discutir noticias del mundo informático, sin sentir orgullo a la vista de la bandera australiana, sin relacionarme con personas con una mente abierta como una holandesa?&lt;/p&gt;
&lt;p&gt;Digo que pertenezco a todos. No voy a elegir. Muchos de los problemas del mundo hoy en día ocurren porque queremos que otros elijan: entre sus países o sus religiones, sus familias o sus sexualidades, sus principios o sus presidentes. La verdad es que no hay una opción sino ser lo que ya son: producto de todos.&lt;/p&gt;
&lt;p&gt;¿Y no somos todos así?&lt;/p&gt;</description></item><item><title>Manufactured Chaos: How Netflix Does Performance Testing</title><link>https://nicolevanderhoeven.com/blog/20180328-manufactured-chaos/</link><pubDate>Wed, 28 Mar 2018 23:35:52 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20180328-manufactured-chaos/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/manufactured-chaos-how-netflix-does-performance-testing"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/manufactured-chaos-how-netflix-does-performance-testing"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It’s always ideal to run performance tests against an environment that’s production-like. That way, you can ensure that your tests aren&amp;rsquo;t being influenced by factors that are not present in production - such as sub-optimal specifications, or the configurations of a test server. So, what does a good performance tester do? They compare their machine specifications and request production-sized boxes to run their tests against. But is that really enough?&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the reality: despite our planning, we still sometimes see things in production that we don’t see, and can’t reproduce, in the test environment. It&amp;rsquo;s hard to predict every possible failure. Even if you do recognize the danger, it’s still difficult to recreate the perfect storm of circumstances that could potentially lead to its occurrence. The more complex your network architecture is, the more likely it is that these failures will occur. As a result, there’s something of a “don’t breathe or it might break” feeling that comes with deploying to production. So how do you test your application to get the maximum degree of confidence in its performance?&lt;/p&gt;
&lt;p&gt;If you’re Netflix, your answer would be to &lt;strong&gt;lean into the chaos&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Chaos theory, in a nutshell, tells us that what looks random actually contains patterns that, while unpredictable, are recognizable and thus manageable. Far from representing disorder, chaos is our word for events that may be infinitely complex and interconnected in ways that aren’t immediately apparent. Chaos engineering takes this principle and applies it to software. Developers can code to reduce the occurrence of chaos as much as possible, but they can also code to make sure applications can withstand chaos if it does occur. As testers, one way we can test the latter is to manufacture chaos.&lt;/p&gt;
&lt;p&gt;Netflix’s testing strategy puts a lot of emphasis on manufacturing chaos because their business model involves streaming gigabits of data to customers on demand, from anywhere in the world, at a consistently fast enough speed that people aren’t annoyed by buffering in the middle of a rousing speech by the Mother of Dragons. In 2015, Netflix accounted for 36% of traffic in &lt;a href="http://fortune.com/2015/10/08/netflix-bandwith/"&gt;North America&lt;/a&gt;. Netflix customers stream 125 million hours of video &lt;a href="https://www.wired.com/2016/03/netflixs-grand-maybe-crazy-plan-conquer-world/"&gt;every day&lt;/a&gt;. Netflix used to serve the content from their own private data centers but moved to the cloud in 2008 after a database corruption disrupted their business for three days. Today, Netflix is 100% in the cloud, which provides tangible benefits such as architecture that can scale on demand thanks to their use of Amazon Web Services (AWS) - incidentally, the same CDN that we use here at Flood IO. However, even the cloud isn’t invulnerable to chaos. Enter the Simian Army.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20180328-01.png" alt="The Simian Army at Netflix" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Conjuring up the humorous image of a squadron of monkeys wreaking havoc in a data center, the Simian Army is a series of tools developed by Netflix that code chaos into their tests - on purpose. At Netflix, every build has to pass not just the standard suite of functional and nonfunctional tests, but also the chaotic-but-programmable tests of the Simian Army, all of which Netflix have made available as open-source tools on Github. Each monkey is a real-world manufactured-chaos test that the application under test must pass in order to be deployed into production with a high degree of confidence in its stability and continued performance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chaos Monkey&lt;/strong&gt; selects a node or container within a node at random and terminates it unexpectedly, forcing Netflix engineers to adapt their code to deal with this behavior by quickly rerouting requests to backup nodes and containers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Janitor Monkey&lt;/strong&gt; detects unused resources (instances, volumes) in the cloud and terminates them. Ideally, all resources no longer used should be shut down, but in practice this doesn’t always happen. People forget to delete things they’re not using anymore. Applications are changed to use new volumes, and old ones are forgotten. While it’s a good idea to make sure everyone is aware of the need to clean up after themselves, Janitor Monkey assumes that things will fall through the cracks and automatically comes to the rescue. Janitor Monkey also sends a notification to the owner of the resource with enough time to mark the resource as an exception before deletion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conformity Monkey&lt;/strong&gt; scans instances and determines whether they conform to a set of rules specified by the organization. If an instance is not configured the right way, Conformity Monkey sends the owner a notification. It can detect things like an instance being created without the appropriate security groups or tags.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security Monkey&lt;/strong&gt; monitors your entire public or private cloud to look for policy changes or incorrectly configured instances that could lead to serious security breaches. It even provides a UI so that you can see a history of changes to important policies and whom they were made by.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Simian Army&lt;/strong&gt; is a marked departure from traditional software testing techniques, which are very much procedural and based on predetermined flows. The assumption is that people behave logically, are all experts in their field, forget nothing, and always follow best practices. But is this actually realistic?&lt;/p&gt;
&lt;p&gt;Netflix’s approach to testing is innovative in that they don’t assume that everyone involved knows exactly what they’re doing, from the developers to the infrastructure engineers to the testers. Neither do they assume that it is always possible to know the exact cause of failures in production. Sometimes bad stuff just hits the fan and nobody sees it coming. The very nature of our work in technology requires constant change, and it can be difficult to keep up with all the ways in which applications are becoming increasingly more integrated with each other. This will only become more apparent as the Internet of Things becomes more popular.&lt;/p&gt;
&lt;p&gt;At &lt;a href="https://flood.io/"&gt;Flood&lt;/a&gt;, we often see our customers targeting production environments, and there are some strong arguments for doing this. Testing later in the lifecycle means we can start to tackle production-sized problems around availability, scalability and reliability that would have otherwise been difficult to reproduce. Let’s face it. We all sometimes see monkeys in our data centers. Maybe it’s time we started planning for it.&lt;/p&gt;
&lt;h2 id="references"&gt;References:&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://media.netflix.com/en/company-blog/completing-the-netflix-cloud-migration."&gt;Completing the Netflix cloud migration&lt;/a&gt;
&lt;a href="http://fractalfoundation.org/resources/what-is-chaos-theory/"&gt;What is chaos theory?&lt;/a&gt;
&lt;a href="http://principlesofchaos.org/"&gt;Principles of chaos engineering&lt;/a&gt;
&lt;a href="https://medium.com/netflix-techblog/the-netflix-simian-army-16e57fbab116"&gt;The Netflix Simian Army&lt;/a&gt;
&lt;a href="https://medium.com/becloudy/chaos-engineering-surviving-the-failures-in-distributed-systems-5688c6905dbb"&gt;Chaos engineering - surviving the failures in distributed systems&lt;/a&gt;
&lt;a href="https://www.linkedin.com/pulse/chaos-engineering-series-part-i-deep-dive-sathiya-shunmugasundaram/"&gt;Chaos engineering series - Part 1: chaos engineering deep dive&lt;/a&gt;
&lt;a href="https://github.com/Netflix/SimianArmy/wiki"&gt;What is Simian Army?&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Why AirBNB Plus Could Make or Break AirBNB</title><link>https://nicolevanderhoeven.com/blog/20180224-why-airbnb-plus/</link><pubDate>Sat, 24 Feb 2018 23:24:00 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20180224-why-airbnb-plus/</guid><description>&lt;p&gt;Originally published on &lt;a href="https://medium.com/@n_vanderhoeven/why-airbnb-plus-could-make-or-break-airbnb-36c39a2c1669"&gt;Medium&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;“We’ve done the anywhere part,” Brian Chesky announced a few days ago, “but AirBNB is still, for some, an alternative way of travelling… until today.”&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.airbnb.com/plus"&gt;AirBNB Plus&lt;/a&gt; is a new tier of service that AirBNB have introduced for homes and hosts that go beyond the Superhost standard.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20180224-01.jpg" alt="" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here’s what an AirBNB Plus listing means:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The home has been verified by an AirBNB representative in person to have passed a 100-point checklist of quality. AirBNB has not yet publicly released the contents of the checklist.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The home comes with a list of extra amenities, such as a fully-stocked kitchen, wireless internet, and even paintings on the wall.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The host has received an average rating of at least 4.8 out of 5 stars.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You will be able to check in reliably at any time of the day. This could be due to an electronic keypad, safe lockbox or simply a host that’s willing to come out and let you in when you’re ready.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You automatically get premium support from a designated AirBNB Plus team in case anything goes wrong.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AirBNB has indicated that for now, only those who are already Superhosts will be eligible to request AirBNB Plus status.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;According to &lt;a href="https://www.theverge.com/2018/2/22/17040684/airbnb-plus-hotels-standard-amenities-service-loyalty-program"&gt;the Verge&lt;/a&gt;, the host needs to pay $149 to apply to be considered for AirBNB Plus status.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AirBNB Plus listings are already available in several cities around the world: Austin, Barcelona, Cape Town, Chicago, Los Angeles, London, Melbourne, Milan, Rome, San Francisco, Shanghai, Sydney and Toronto, so you can &lt;a href="https://www.airbnb.com/s/select_homes"&gt;go check them out yourself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the surface, this sounds like a great idea. I almost always travel with AirBNB, and when I’ve chosen to book a typical hotel, it’s always because I couldn’t find a Superhost listing to book. I’ve had bad experiences with AirBNB in the past, and have since decided to only book with Superhosts. However, even that is not a real guarantee of quality — in contrast to AirBNB Plus, becoming a Superhost has much less stringent rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here’s what a Superhost listing means:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The host has received an average of at least a 4.8-star rating for all stays.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The host reponds to messages within 24 hours at least 90% of the time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At least 10 guests have stayed at the property in the last year.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The host has never cancelled a reservation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s almost entirely review-based: the requirement to maintain at least a 4.8-star rating is the most difficult hurdle. There are otherwise no quality requirements that the listing must maintain. In effect, Superhosts are community-selected, which is tricky because a string of teenagers used to hostels and leaving glowing reviews on a listing could make it appear more attractive than it actually is. I’ve stayed with Superhosts that had faulty showers, unstable internet and a kitchen that consisted of a sink and a kettle.&lt;/p&gt;
&lt;p&gt;So I really like the idea of a higher class of accommodations that have been independently inspected. This is the first time they have ever offered anything like a guarantee for the quality of their things. Previously there was still a bit of a risk with booking an AirBNB, but this should dispel all doubt. AirBNB is doing what every company does after its initial growth spurt: it’s bunkering down, standardising its product… and, in effect, increasing some of its prices.&lt;/p&gt;
&lt;p&gt;While price increases are not explicitly mentioned, it seems reasonable to expect that listings in this new category would fetch higher prices. After all, it costs more labour on AirBNB’s part to inspect these listings and it also costs the host at least $149, not to mention extra costs spent to meet the 100-point checklist. AirBNB Plus is no doubt a premium offering, catered towards a wealthier clientele.&lt;/p&gt;
&lt;p&gt;Another concern is that the line between AirBNBs and hotels are blurring, and not necessarily in a good way. The original appeal of an AirBNB was that it was someone’s personal home, with all the quirks and defects that come with it. AirBNB’s overwhelming success then brought about a new class of landlords who buy properties with the specific intention to rent them via the platform. Already there are several boutique hotels that have been allowed to list accommodations as well.&lt;/p&gt;
&lt;p&gt;The new Plus tier seems to narrow the gap between corporate hotel chains and spare rooms owned by real locals substantially. This feels less like a disruption of the hotel industry and more like a return to the status quo. I remain a huge fan of the brand, and I worry that AirBNB will lose that disruptive “plucky startup” charm — because they’re no longer a startup. They’re a major player, and if this announcement is anything to go by, they’re about to get even bigger.&lt;/p&gt;</description></item><item><title>Should I Upgrade to JMeter 4.0? 5 Reasons to Upgrade</title><link>https://nicolevanderhoeven.com/blog/20180223-should-i-upgrade-to-jmeter-4/</link><pubDate>Fri, 23 Feb 2018 23:43:25 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20180223-should-i-upgrade-to-jmeter-4/</guid><description>
&lt;link rel="canonical" href="https://www.flood.io/blog/should-i-upgrade-to-jmeter-4-0-5-reasons-to-upgrade"&gt;
&lt;p&gt;&lt;em&gt;Originally posted &lt;a href="https://www.flood.io/blog/should-i-upgrade-to-jmeter-4-0-5-reasons-to-upgrade"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Apache has just launched &lt;a href="http://jmeter.apache.org/download_jmeter.cgi"&gt;JMeter 4.0&lt;/a&gt;, a big step up from their previous release, 3.3, which was only released in September last year. Apache seems to be stepping up the frequency of it’s releases, and we’re not complaining. JMeter is an amazing open source load testing tool and the most popular one used at &lt;a href="http://flood.io/"&gt;Tricentis Flood&lt;/a&gt; by far.&lt;/p&gt;
&lt;p&gt;The big question is: Should you upgrade? Are the new features worth the leap? Will the new black UI make you a better load tester? While this choice should be made on a case-by-case basis and we certainly can’t pretend to give one-size-fits-all advice, here are some good arguments to do it:&lt;/p&gt;
&lt;h3 id="1-java-9-support"&gt;#1: Java 9 support&lt;/h3&gt;
&lt;p&gt;JDK 9 was released back in September 2017, and if you were one of the people who upgraded to it when prompted, you would have noticed to your frustration that JMeter 3.3 did not play well with it, yielding the error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Error: Java version -- 9.0.1 -- is too low to run JMeter. Needs a Java version greater than or equal to 1.8.0 errorlevel&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Good news! This is now fixed. JMeter 4.0 runs smoothly on JDK 9. We&amp;rsquo;ll continue running on JDK 8 at Flood for consistency amongst all our open source load testing tools.&lt;/p&gt;
&lt;h3 id="2-new-boundary-extractor"&gt;#2: New Boundary Extractor&lt;/h3&gt;
&lt;p&gt;Previously, my preferred way to extract dynamic values in JMeter was to use a regular expression extractor, but there’s no doubt that it can sometimes be tricky to get just the right text out of a response. Enter the &lt;a href="http://jmeter.apache.org/usermanual/component_reference.html#Boundary_Extractor"&gt;Boundary Extractor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The boundary extractor allows you to simply set the text immediately before and after the text you want to extract. This saves you wondering whether you should have used a + or a * in your regular expression, and is also easier to validate.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20180223-01.png" alt="Boundary Extractor in JMeter" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="3-new-json-assertion-element"&gt;#3: New JSON assertion element&lt;/h3&gt;
&lt;p&gt;While XPath assertion was great, there was previously no counterpart for JSON. The &lt;a href="http://jmeter.apache.org/usermanual/component_reference.html#JSON_Assertion"&gt;JSON Assertion&lt;/a&gt; element fills that void nicely, using &lt;a href="https://github.com/json-path/JsonPath"&gt;JsonPath&lt;/a&gt; to quickly assert the existence (or non-existence, with the Invert assertion option ticked) of a particular element.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20180223-02.png" alt="JSON Assertion element in JMeter" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="4-new-throughput-timer"&gt;#4: New throughput timer&lt;/h3&gt;
&lt;p&gt;We have a new throughput timer to play with, and it’s called &lt;a href="http://home.apache.org/~milamber/jmeter-4.0RC4/docs/usermanual/component_reference.html#Precise_Throughput_Timer"&gt;Precise Throughput Timer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20180223-03.png" alt="Precise Throughput Timer in JMeter" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Whereas the Constant Throughput Timer also had the disadvantage of constant pacing of requests, the Precise Throughput Timer is based on the &lt;a href="http://home.apache.org/~milamber/jmeter-4.0RC4/docs/usermanual/component_reference.html#Poisson_Random_Timer"&gt;Poisson Random Timer&lt;/a&gt; and randomly generates the delay between each request. The implementation seems to be well thought out, with the component reference section listing a lot of scenarios they’ve tested for that the previous Constant Throughput Timer might have struggled with. Throughput timers are one of my favourite JMeter features, and still rather unique in the industry.&lt;/p&gt;
&lt;h3 id="5-test-plan-autosave"&gt;#5: Test Plan Autosave&lt;/h3&gt;
&lt;p&gt;This one’s more of a usability tweak, but it’s a good one. JMeter now automatically saves your script when you run a test, saving you from those all-too-familiar “Wait… did I CTRL+S before I hit CTRL+R?” moments.&lt;/p&gt;
&lt;p&gt;Click &lt;a href="http://home.apache.org/~milamber/jmeter-4.0RC4/docs/changes.html"&gt;here&lt;/a&gt; to see the full release notes for version.&lt;/p&gt;
&lt;h3 id="bonus-reason-tricentis-flood-support"&gt;BONUS reason: Tricentis Flood support&lt;/h3&gt;
&lt;p&gt;You can now upload JMeter 4.0 scripts to flood.io and be up and running with your first test hosted on one of our grid nodes around the world. Our customer support team is always happy to help you make the switch to 4.0.&lt;/p&gt;</description></item><item><title>How I Became Financially Independent in 5 Years</title><link>https://nicolevanderhoeven.com/blog/20180222-how-i-became-financially-independent-in-5-years/</link><pubDate>Thu, 22 Feb 2018 11:40:32 +0000</pubDate><guid>https://nicolevanderhoeven.com/blog/20180222-how-i-became-financially-independent-in-5-years/</guid><description>&lt;p&gt;Here’s my secret: I’m 32 years old and I am financially independent. And my husband and I did it all, from $0 to where we’re at now, in 5 years.&lt;/p&gt;
&lt;h2 id="what-is-financial-independence"&gt;What is financial independence?&lt;/h2&gt;
&lt;p&gt;But let’s back up. Financial independence means I no longer &lt;em&gt;need&lt;/em&gt; to work to support myself and my husband. This does not mean I’m filthy rich; this means only that if I stopped working right now, we would be able to continue living a simple life without needing to worry about paying the bills. If you actually do the calculations, that number is not as high as you might think.&lt;/p&gt;
&lt;p&gt;Still, it’s a huge financial milestone that gives me a lot of freedom. Think about it: if you have enough money to cover my basic living expenses, why would you choose a job you don’t like? Why would you spend 8+ hours of my day, 5 days a week, with people whose company you don’t genuinely enjoy? It’s called financial &lt;em&gt;independence&lt;/em&gt; because it’s the end of slavery — slavery to the rat race and to the whims of the corporate world. Financial independence gives you the freedom to design and pursue the life you actually want.&lt;/p&gt;
&lt;h2 id="where-we-started"&gt;Where we started&lt;/h2&gt;
&lt;p&gt;When my husband Rob and I moved in together, we had a negative net worth. We had his three teenage children living with us and neither of us had a job. We lived in a small town that didn’t have many job opportunities in his industry and I, with my Australian permanent residency still in progress, was not allowed to work in Australia. Finally Rob found a job in Melbourne, and we moved the entire family there. We signed the lease on a ridiculously expensive rental house in order to get the kids in the right zone for the best high school we could afford. Despite Rob’s income, we struggled, borrowing from credit cards to finance expenses we just couldn’t seem to get on top of.&lt;/p&gt;
&lt;p&gt;Eventually I got my permanent residency and a job. Even though my income could just barely cover the rent, that money made us feel rich. As the years went by and my salary increased, so did our expenses. We had lunch out nearly every day while we threw out spoiled groceries at home. We went to several concerts and theatres, some of them twice or thrice. We bought a car — on loan, of course. Almost our entire paychecks were immediately spent paying off the credit card bills from the last month; then we’d use the same credit cards to pay off this month’s expenses. In short, we were incredibly irresponsible. Still, thanks to dual higher-than-average IT incomes, we managed to convince a bank to give us a mortgage. Despite our incomes we could only afford a laughable 2% downpayment on the house — while we still owed money on credit cards.&lt;/p&gt;
&lt;p&gt;Year 0 actually begins the day Rob’s current project ended. He was a contractor, so no project meant no job. Knowing we’d have to live tight on just my salary to meet the mortgage and our monthly living expenses, I started to search for budgeting tips online. What I found would eventually lead us down the road to financial independence. I took control of our finances and changed them around. Here’s what I did.&lt;/p&gt;
&lt;h2 id="what-we-did"&gt;What we did&lt;/h2&gt;
&lt;h3 id="i-consolidated-all-our-accounts"&gt;I consolidated all our accounts**.&lt;/h3&gt;
&lt;p&gt;We had several transaction accounts, savings account and credit cards, and the first step was to have fewer buckets for our money. Having fewer accounts made it easier to keep track of everything, especially in the beginning, and it also meant it was easier to calculate our (then negative) net worth and track changes to it.&lt;/p&gt;
&lt;h3 id="i-used-the-envelope-system-for-budgeting-to-keep-a-lid-on-our-daily-expenses"&gt;I used the envelope system for budgeting to keep a lid on our daily expenses.&lt;/h3&gt;
&lt;p&gt;Specifically, I used an app called You Need a Budget (YNAB — and that’s an affiliate link), but you don’t need to buy it (although I personally think it’s more than worth it). I tracked every single cent then, and in fact I still do that today. Instead of having one account and one balance to look at, I created categories for every conceivable expense. Before I found YNAB, I just used an Excel spreadsheet. A big shift in habit was when I started to look at the category balance instead of the account balance. If we wanted to buy something that cost more than the budget for its category, we would look to see what other categories we could take the money out of. This helped us understand that every time we said yes to buying something, we were saying no to buying something else. Even if we paid using the credit card, that money still went out of the category balance so that we could at least make sure we weren’t adding to our credit card debt.&lt;/p&gt;
&lt;h3 id="i-automated-transfers-to-pay-back-credit-card-debt-and-to-our-savings-account"&gt;I automated transfers to pay back credit card debt and to our savings account.&lt;/h3&gt;
&lt;p&gt;I discussed with Rob an amount that we would save out of my salary every month for emergencies and I set up an automatic transfer from our normal transaction account to our separate savings account with another bank the day after payday. The automatic part is important — you don’t want to give yourself the chance to self-sabotage. This made sure that even if we went over budget, we would not touch our savings. I did the same for extra debt payments towards credit cards and our car loan until they were competely paid off. I also saved one month’s worth of household expenses as an emergency fund.&lt;/p&gt;
&lt;h2 id="the-waiting-game"&gt;The waiting game&lt;/h2&gt;
&lt;p&gt;By the time Rob found a job again, we were debt free except for our mortgage. We suddenly had a lot of cash to spare, both from his salary and from paying off debts. As soon as that happened, rather than increasing our expenses again, I automated transfers to investment accounts instead. For us in Australia, this meant talking to our employers to salary sacrifice into our superannuation funds, for you, this could be contributing to your 401k. We started salary sacrificing as much as we could per year (at the time, that was $30,000 for me and $35,000 for Rob including employer contributions, but check the current limits with the ATO as they have changed). This was a huge deal because we never even saw the money as it was garnished from our wages. It also saved us heaps of tax (one year I calculated it to be about $8000 in savings for Rob alone). At this point we were saving 70% of our income.&lt;/p&gt;
&lt;p&gt;When we got unexpected income increases or tax refunds, we put it towards investments. As our incomes grew, we maxed out our super contributions and started to look for something else to invest in. I invested our money into a low cost index fund, available from any stock broker. In our case, we used Vanguard’s VAS for Australian shares. Again, those payments were automated and transferred regardless of whether the stock market was doing well or not.&lt;/p&gt;
&lt;p&gt;Then, we waited.&lt;/p&gt;
&lt;h2 id="the-result"&gt;The result&lt;/h2&gt;
&lt;p&gt;Within five years we had nearly enough to meet our subsistence-level figure. Some surprise financial windfalls finally pushed us over the edge.&lt;/p&gt;
&lt;p&gt;I’m sharing this story not to brag about my successes but to let people know that financial independence is not just possible but SIMPLE.&lt;/p&gt;
&lt;p&gt;Let me break down the steps again for you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Calculate your financial net worth.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Revisit and optimise all your accounts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Track every single expense for at least a month.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make a budget.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Automate transfers to improve your net worth: first towards your debt, then savings towards an emergency fund and then to an investment account.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wait.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now I’m not saying that everyone can do this in five years. My husband and I were lucky enough to have dual high incomes. But the point is not to do all of this in five years. The point is to start now.&lt;/p&gt;</description></item></channel></rss>