<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Neeraj Das</title><link>https://neerajdas.com/</link><description>Recent content on Neeraj Das</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 02 Mar 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://neerajdas.com/feed.xml" rel="self" type="application/rss+xml"/><item><title>Foodies (web app)</title><link>https://neerajdas.com/ux/foodies-web/</link><pubDate>Thu, 02 Mar 2023 00:00:00 +0000</pubDate><guid>https://neerajdas.com/ux/foodies-web/</guid><description>&lt;div class="d-flex justify-content-center mb-5">
&lt;img src="https://neerajdas.com/assets/images/foodies_desktop/foodies_desktop.png" class="img-fluid" alt="Foodies image"
style="max-width: 1200px; width: 100%;">
&lt;/div>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>This case study focuses on designing a responsive web app for the existing &amp;ldquo;Foodies&amp;rdquo; food delivery mobile app, as described in this &lt;a href="https://neerajdas.com/ux/foodies/">case study&lt;/a>.&lt;/p>
&lt;p>It targets customers who prefer devices with large screen sizes (laptops, tablets or desktops) to mobile devices.&lt;/p>
&lt;br>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-exclamation-circle-fill">&lt;/i> The Problem&lt;/h4>
&lt;p>
Some people prefer devices with large screen sizes for online activities.
&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-star-fill">&lt;/i> The Goal&lt;/h4>
&lt;p>Design a food delivery responsive web app that allows users to easily order healthy meals and get it delivered at their doorstep.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-person-circle">&lt;/i> My Role&lt;/h4>
&lt;p>UX designer designing a food delivery responsive web app from conception to delivery.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-body-text">&lt;/i> Responsibilities&lt;/h4>
&lt;p>Conducting interviews, paper and digital wireframing, low and high-fidelity prototyping, conducting usability studies, accounting for accessibility, and iterating on designs.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="user-research">User research&lt;/h2>
&lt;p>I conducted interviews and created empathy maps to understand the users&amp;rsquo; problems. The primary user group were people who preferred devices with large screen sizes for online activities.&lt;/p>
&lt;p>This user group confirmed initial assumptions about potential customers. Research also revealed that apart from screen size, the availability of a mobile device was also a factor limiting users from using the mobile app.&lt;/p>
&lt;br>
&lt;h3 id="pain-points">Pain points&lt;/h3>
&lt;h4>&lt;i class="bi bi-universal-access">&lt;/i> Accessibility&lt;/h4>
&lt;p>Either the target users don't have access to a mobile device or have difficulties using one.&lt;/p>
&lt;h3 id="persona">Persona&lt;/h3>
&lt;div class="row border border-purple px-2 py-4">
&lt;div class="col-md-4 d-flex flex-column align-items-center">
&lt;img src="https://neerajdas.com/assets/images/foodies/pratyush.jpg" class="img-fluid rounded-circle" alt="Blood donor app wireframe"
style="max-width: 200px; width: 100%;">
&lt;b>Pratyush&lt;/b>
&lt;div class="pt-2">
&lt;label class="d-block">&lt;b>Age:&lt;/b> 31&lt;/label>
&lt;label class="d-block">&lt;b>Education:&lt;/b> MBBS&lt;/label>
&lt;label class="d-block">&lt;b>Occupation:&lt;/b> Doctor&lt;/label>
&lt;label class="d-block">&lt;b>Hometown:&lt;/b> Sambalpur, Odisha&lt;/label>
&lt;/div>
&lt;/div>
&lt;div class="col-md-8">
&lt;i>"Healing the world and searching for tools that can help me live a better life"&lt;/i>
&lt;div class="row py-3">
&lt;div class="col-md-6">
&lt;b>Goals&lt;/b>
&lt;ul>
&lt;li>Make time for each of his patients&lt;/li>
&lt;li>To have easy access to healthy food&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="col-md-6">
&lt;b>Frustrations&lt;/b>
&lt;ul>
&lt;li>Having a busy schedule makes it difficult for him to prepare a meal&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div>
Pratyush is a Doctor with a busy and demanding schedule at a hospital in Sambalpur.
He cares a lot about being available to his patients and often takes appointments at the
last minute. He lives alone and mostly orders food online. Text-heavy and unresponsive apps
make it difficult for him to find and order the right food.
&lt;/div>
&lt;/div>
&lt;/div>
&lt;br>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="starting-the-design">Starting the design&lt;/h2>
&lt;h3 id="wireframes">Wireframes&lt;/h3>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies_desktop/wireframes.png" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;br>
&lt;h3 id="low-fidelity-prototype">Low-fidelity prototype&lt;/h3>
&lt;p>Using the completed set of digital wireframes, I created a low-fidelity prototype. The primary user flow was to select a restaurant and order food. This prototype was used in a usability study.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies_desktop/lofi.png" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="usability-study">Usability study&lt;/h2>
&lt;p>I used the high-fidelity prototype for the usability study and it revealed aspects of the designs that needed refining.&lt;/p>
&lt;h3 id="findings">Findings&lt;/h3>
&lt;ul>
&lt;li>Menu tab was missing from the restaurant page.&lt;/li>
&lt;li>Important links: Help, Contact and About were missing from the design.&lt;/li>
&lt;/ul>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="refining-the-design">Refining the design&lt;/h2>
&lt;p>I added the Menu tab and the missing links.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies_desktop/restaurant.png" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="high-fidelity-prototype">High fidelity prototype&lt;/h2>
&lt;p>The final high-fidelity prototype presented cleaner user flows for ordering food and checkout. It also met user needs for payment on delivery option.&lt;/p>
&lt;p>&lt;a href="https://www.figma.com/proto/W6EntDiwAN7SqHDTkR2mUZ/Foodies?node-id=545%3A1857&amp;scaling=scale-down&amp;page-id=545%3A1856&amp;starting-point-node-id=545%3A1857"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Play prototype
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies_desktop/hifi.png" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="accessibility-considerations">Accessibility considerations&lt;/h2>
&lt;ul>
&lt;li>Provided access to users who are visually impaired through adding alt text to images for screen readers.&lt;/li>
&lt;li>Used icons to help make navigation easier.&lt;/li>
&lt;li>Used detailed imagery for food items to help all users better understand the designs.&lt;/li>
&lt;/ul>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="takeaways">Takeaways&lt;/h2>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h5>Impact&lt;/h5>
&lt;p>Users love the web version of the food delivery mobile app.&lt;/p>
&lt;h5>Quote from peer feedback&lt;/h5>
&lt;p>&lt;i>"Wow, now I don't have to switch devices to order food while working on my laptop. Big thanks to the Foodies team for thinking about this."&lt;/i>&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h5>What I learned&lt;/h5>
&lt;p>While designing this app, I learned that the first ideas for an app are only the beginning of the process. Usability studies and peer feedback influenced each iteration of the app's designs.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="next-steps">Next steps&lt;/h2>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-1-circle-fill">&lt;/i>&lt;/h4>
&lt;p>Conduct another round of usability studies to validate whether the pain points users experienced have been effectively addressed.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-2-circle-fill">&lt;/i>&lt;/h4>
&lt;p>Conduct more user research to determine any new areas of need.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg></description></item><item><title>Blood donor</title><link>https://neerajdas.com/ux/blood-donor/</link><pubDate>Tue, 21 Feb 2023 00:00:00 +0000</pubDate><guid>https://neerajdas.com/ux/blood-donor/</guid><description>&lt;div class="d-flex justify-content-center mb-5">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/blood_donor.png" class="img-fluid" alt="Blood donor app screenshot"
style="max-width: 1200px; width: 100%;">
&lt;/div>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&amp;ldquo;Blood Donor&amp;rdquo; is a mobile app designed to help people donate or find blood donors. It targets people who have difficulties finding a blood donor.&lt;/p>
&lt;br>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-exclamation-circle-fill">&lt;/i> The Problem&lt;/h4>
&lt;p>People have difficulties finding blood donors.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-star-fill">&lt;/i> The Goal&lt;/h4>
&lt;p>Design an app to help people donate or find blood donors easily.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-person-circle">&lt;/i> My Role&lt;/h4>
&lt;p>UX designer designing a blood donation and procurement app from conception to delivery.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-body-text">&lt;/i> Responsibilities&lt;/h4>
&lt;p>Conducting interviews, wireframing, low and high-fidelity prototyping, conducting usability studies, accounting for accessibility, and iterating on designs.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="user-research">User research&lt;/h2>
&lt;p>I conducted interviews and created empathy maps to understand the users&amp;rsquo; problems. The primary user group were people who had faced a situation where they needed a blood donor.&lt;/p>
&lt;br>
&lt;h3 id="pain-points">Pain points&lt;/h3>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-binoculars-fill">&lt;/i> Finding donors&lt;/h4>
&lt;ul>
&lt;li>Posting on multiple social platforms is tiring, consumes a lot of time and doesn't give a positive response.&lt;/li>
&lt;li>Unable to find donors who are nearby or available.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-patch-question-fill">&lt;/i> Availability&lt;/h4>
&lt;p>Unaware of the availability of blood, travelling to different blood banks is a pain.&lt;/p>
&lt;/div>
&lt;/div>
&lt;br>
&lt;h3 id="persona">Persona&lt;/h3>
&lt;div class="row border border-purple px-2 py-4">
&lt;div class="col-md-4 d-flex flex-column align-items-center">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/nancy.jpg" class="img-fluid rounded-circle" alt="Blood donor app wireframe"
style="max-width: 200px; width: 100%;">
&lt;b>Nancy&lt;/b>
&lt;div class="pt-2">
&lt;label class="d-block">&lt;b>Age:&lt;/b> 26&lt;/label>
&lt;label class="d-block">&lt;b>Education:&lt;/b> B Ed&lt;/label>
&lt;label class="d-block">&lt;b>Occupation:&lt;/b> Teacher&lt;/label>
&lt;label class="d-block">&lt;b>Hometown:&lt;/b> Bhubaneswar&lt;/label>
&lt;/div>
&lt;/div>
&lt;div class="col-md-8">
&lt;i>"Looking for a tool that can help me find blood donors near me"&lt;/i>
&lt;div class="row py-3">
&lt;div class="col-md-6">
&lt;b>Goals&lt;/b>
&lt;ul>
&lt;li>To find blood on time&lt;/li>
&lt;li>To find the right blood match&lt;/li>
&lt;li>Easily connect to a donor&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="col-md-6">
&lt;b>Frustrations&lt;/b>
&lt;ul>
&lt;li>Trouble finding donors&lt;/li>
&lt;li>Unavailability of blood&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div>
Nancy is a teacher at a primary school in Bhubaneswar. She is suffering from a chronic illness which requires frequent blood transfusions. She lives with her parents and hopes to find an easy way to find blood donors during her treatment.
&lt;/div>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="starting-the-design">Starting the design&lt;/h2>
&lt;h3 id="wireframes">Wireframes&lt;/h3>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/wireframes.png" class="img-fluid" alt="Blood donor app wireframe"
style="max-width: 1200px; width: 100%;">
&lt;/div>
&lt;br>
&lt;h3 id="low-fidelity-prototype">Low-fidelity prototype&lt;/h3>
&lt;p>Using the completed set of digital wireframes, I created a low-fidelity prototype. The primary user flow was to find a blood donor. This prototype was used in a usability study.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/lofi.png" class="img-fluid" alt="Blood donor app low fidelity prototype image"
style="max-width: 1200px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="usability-study">Usability study&lt;/h2>
&lt;p>I conducted two rounds of usability studies. Findings from the first study helped guide the designs from wireframes to mockups. The second study used a high-fidelity prototype and revealed what aspects of the mockups needed refining.&lt;/p>
&lt;h3 id="round-1-findings">Round 1 findings&lt;/h3>
&lt;p>The design didn&amp;rsquo;t include a sign-in process for recipients to help find a blood donor fast. Because of this, users couldn&amp;rsquo;t track their requests after closing the app.&lt;/p>
&lt;h3 id="round-2-findings">Round 2 findings&lt;/h3>
&lt;p>The &amp;ldquo;blood group&amp;rdquo; and &amp;ldquo;state&amp;rdquo; appeared to be text input fields which should have been dropdown menus.&lt;/p>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="refining-the-design">Refining the design&lt;/h2>
&lt;p>I added a simple phone number and OTP sign-in process to help users track their requests.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/login.png" class="img-fluid" alt="Foodies image"
style="max-width: 820px; width: 100%;">
&lt;/div>
&lt;p>I added icons to make &amp;ldquo;blood group&amp;rdquo; and &amp;ldquo;state&amp;rdquo; appear like dropdown menus.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/form.png" class="img-fluid" alt="Foodies image"
style="max-width: 820px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="high-fidelity-prototype">High fidelity prototype&lt;/h2>
&lt;p>The final high-fidelity prototype presented cleaner user flows for finding a blood donor.&lt;/p>
&lt;p>&lt;a href="https://www.figma.com/proto/XCwGka9bxhZJg4w2ALasZs/Blood-Donor?node-id=24%3A200&amp;scaling=scale-down&amp;page-id=0%3A1&amp;starting-point-node-id=24%3A200"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Play prototype
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/hifi.png" class="img-fluid" alt="Blood donor app high fidelity prototype image"
style="max-width: 1200px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="takeaways">Takeaways&lt;/h2>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h5>Impact&lt;/h5>
&lt;p>The app makes users feel the "Blood donors" app does a nice job finding a blood donor.&lt;/p>
&lt;h5>Quote from peer feedback&lt;/h5>
&lt;p>&lt;i>"The app made it so easy and quick to find a blood donor. I would definitely use this app."&lt;/i>&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h5>What I learned&lt;/h5>
&lt;p>While designing this app, I learned that the first ideas for an app are only the beginning of the process. Usability studies and peer feedback influenced each iteration of the app's designs.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="next-steps">Next steps&lt;/h2>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-1-circle-fill">&lt;/i>&lt;/h4>
&lt;p>Conduct another round of usability studies to validate whether the pain points users experienced have been effectively addressed.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-2-circle-fill">&lt;/i>&lt;/h4>
&lt;p>Conduct more user research to determine any new areas of need.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg></description></item><item><title>Foodies</title><link>https://neerajdas.com/ux/foodies/</link><pubDate>Tue, 21 Feb 2023 00:00:00 +0000</pubDate><guid>https://neerajdas.com/ux/foodies/</guid><description>&lt;div class="d-flex justify-content-center mb-5">
&lt;img src="https://neerajdas.com/assets/images/foodies/foodies.png" class="img-fluid" alt="Foodies image"
style="max-width: 1200px; width: 100%;">
&lt;/div>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>Foodies is a food delivery mobile app. It strives to deliver a wide range of food items from various restaurants. It targets customers who lack the time to prepare meal and helps them order food online and get it at their doorstep.&lt;/p>
&lt;br>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-exclamation-circle-fill">&lt;/i> The Problem&lt;/h4>
&lt;p>Busy workers and commuters lack the time necessary to prepare a meal.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-star-fill">&lt;/i> The Goal&lt;/h4>
&lt;p>Design a food delivery app that allows users to easily order healthy meals and get it delivered at their doorstep.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-person-circle">&lt;/i> My Role&lt;/h4>
&lt;p>UX designer designing a food delivery app from conception to delivery.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-body-text">&lt;/i> Responsibilities&lt;/h4>
&lt;p>Conducting interviews, paper and digital wireframing, low and high-fidelity prototyping, conducting usability studies, accounting for accessibility, and iterating on designs.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="user-research">User research&lt;/h2>
&lt;p>I conducted interviews and created empathy maps to understand the users I’m
designing for and their needs. A primary user group identified through research
was working adults who don’t have time to cook meals.&lt;/p>
&lt;p>This user group confirmed initial assumptions about potential customers, but research
also revealed that time was not the only factor limiting users from cooking at home.
Other user problems included obligations, interests, or challenges that make it
difficult to get groceries for cooking or go to restaurants in-person.&lt;/p>
&lt;br>
&lt;h3 id="pain-points">Pain points&lt;/h3>
&lt;div class="row">
&lt;div class="col-md-4">
&lt;h4>&lt;i class="bi bi-clock-fill">&lt;/i> Time&lt;/h4>
&lt;p>Working adults are too busy to spend time on meal prep.&lt;/p>
&lt;/div>
&lt;div class="col-md-4">
&lt;h4>&lt;i class="bi bi-universal-access">&lt;/i> Accessibility&lt;/h4>
&lt;p>Platforms for ordering food are not equipped with assistive technologies.&lt;/p>
&lt;/div>
&lt;div class="col-md-4">
&lt;h4>&lt;i class="bi bi-diagram-3-fill">&lt;/i> Information Architecture&lt;/h4>
&lt;p>Text-heavy menus in apps are often difficult to read and order from.&lt;/p>
&lt;/div>
&lt;/div>
&lt;br>
&lt;h3 id="persona">Persona&lt;/h3>
&lt;div class="row border border-purple px-2 py-4">
&lt;div class="col-md-4 d-flex flex-column align-items-center">
&lt;img src="https://neerajdas.com/assets/images/foodies/pratyush.jpg" class="img-fluid rounded-circle" alt="Blood donor app wireframe"
style="max-width: 200px; width: 100%;">
&lt;b>Pratyush&lt;/b>
&lt;div class="pt-2">
&lt;label class="d-block">&lt;b>Age:&lt;/b> 31&lt;/label>
&lt;label class="d-block">&lt;b>Education:&lt;/b> MBBS&lt;/label>
&lt;label class="d-block">&lt;b>Occupation:&lt;/b> Doctor&lt;/label>
&lt;label class="d-block">&lt;b>Hometown:&lt;/b> Sambalpur, Odisha&lt;/label>
&lt;/div>
&lt;/div>
&lt;div class="col-md-8">
&lt;i>"Healing the world and searching for tools that can help me live a better life"&lt;/i>
&lt;div class="row py-3">
&lt;div class="col-md-6">
&lt;b>Goals&lt;/b>
&lt;ul>
&lt;li>Make time for each of his patients&lt;/li>
&lt;li>To have easy access to healthy food&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="col-md-6">
&lt;b>Frustrations&lt;/b>
&lt;ul>
&lt;li>Having a busy schedule makes it difficult for him to prepare a meal&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div>
Pratyush is a Doctor with a busy and demanding schedule at a hospital in Sambalpur.
He cares a lot about being available to his patients and often takes appointments at the
last minute. He lives alone and mostly orders food online. Text-heavy and unresponsive apps
make it difficult for him to find and order the right food.
&lt;/div>
&lt;/div>
&lt;/div>
&lt;br>
&lt;h3 id="user-journey-map">User journey map&lt;/h3>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies/user_journey.png" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;p>Mapping Pratyush’s user journey revealed how helpful it would be for users to have access to a food delivery app.&lt;/p>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="starting-the-design">Starting the design&lt;/h2>
&lt;h3 id="paper-wireframes">Paper wireframes&lt;/h3>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies/paper_wireframes.jpg" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;p>Taking the time to draft iterations of each screen of the app on paper ensured that the elements that made it to digital wireframes would be well-suited to address user pain points. For the home screen, I prioritized a quick and easy ordering process to help users save time.&lt;/p>
&lt;br>
&lt;h3 id="digital-wireframes">Digital wireframes&lt;/h3>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies/digital_wireframe.png" class="img-fluid" alt="Foodies image"
style="max-width: 650px; width: 100%;">
&lt;/div>
&lt;p>As the initial design phase continued, I made sure to base screen designs on feedback and findings from the user research.&lt;/p>
&lt;br>
&lt;h3 id="low-fidelity-prototype">Low-fidelity prototype&lt;/h3>
&lt;p>Using the completed set of digital wireframes, I created a low-fidelity prototype. The primary user flow I connected was selecting a restaurant and ordering food, so the prototype could be used in a usability study.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies/lofi.png" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="usability-study">Usability study&lt;/h2>
&lt;p>I conducted two rounds of usability studies. Findings from the first study helped guide the designs from wireframes to mockups. The second study used a low-fidelity prototype and revealed what aspects of the mockups needed refining.&lt;/p>
&lt;h3 id="round-1-findings">Round 1 findings&lt;/h3>
&lt;ul>
&lt;li>Users want to be able to edit quantity of food items from the review page as well&lt;/li>
&lt;/ul>
&lt;h3 id="round-2-findings">Round 2 findings&lt;/h3>
&lt;ul>
&lt;li>Sign In link was missing from the Sign Up page&lt;/li>
&lt;/ul>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="refining-the-design">Refining the design&lt;/h2>
&lt;p>Early designs didn’t have a way to edit quantity from the Review page, but after the usability studies, I updated the Review order page to allow Quantity adjustments.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies/review_before_after.png" class="img-fluid" alt="Foodies image"
style="max-width: 650px; width: 100%;">
&lt;/div>
&lt;p>The second usability study revealed that the Sign Up page didn’t have a Sign In link. Added it.&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies/signup_before_after.png" class="img-fluid" alt="Foodies image"
style="max-width: 650px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="high-fidelity-prototype">High fidelity prototype&lt;/h2>
&lt;p>The final high-fidelity prototype presented cleaner user flows for ordering food and checkout. It also met user needs for payment on delivery option.&lt;/p>
&lt;p>&lt;a href="https://www.figma.com/proto/W6EntDiwAN7SqHDTkR2mUZ/Foodies?node-id=238%3A1009&amp;scaling=scale-down&amp;page-id=238%3A1008&amp;starting-point-node-id=238%3A1009"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Play prototype
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>&lt;/p>
&lt;div class="d-flex justify-content-center mb-3">
&lt;img src="https://neerajdas.com/assets/images/foodies/hifi.png" class="img-fluid" alt="Foodies image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="accessibility-considerations">Accessibility considerations&lt;/h2>
&lt;ul>
&lt;li>Provided access to users who are visually impaired through adding alt text to images for screen readers.&lt;/li>
&lt;li>Used icons to help make navigation easier.&lt;/li>
&lt;li>Used detailed imagery for food items to help all users better understand the designs.&lt;/li>
&lt;/ul>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="takeaways">Takeaways&lt;/h2>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h5>Impact&lt;/h5>
&lt;p>The app makes users feel the "Foodies" team thinks about how to meet their needs.&lt;/p>
&lt;h5>Quote from peer feedback&lt;/h5>
&lt;p>&lt;i>"The app made it so easy to select a restaurant and order food! I would definitely use this app for a delicious, fast, and healthy meal."&lt;/i>&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h5>What I learned&lt;/h5>
&lt;p>While designing this app, I learned that the first ideas for an app are only the beginning of the process. Usability studies and peer feedback influenced each iteration of the app's designs.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;h2 id="next-steps">Next steps&lt;/h2>
&lt;div class="row">
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-1-circle-fill">&lt;/i>&lt;/h4>
&lt;p>Conduct another round of usability studies to validate whether the pain points users experienced have been effectively addressed.&lt;/p>
&lt;/div>
&lt;div class="col-md-6">
&lt;h4>&lt;i class="bi bi-2-circle-fill">&lt;/i>&lt;/h4>
&lt;p>Conduct more user research to determine any new areas of need.&lt;/p>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg></description></item><item><title>UX Design</title><link>https://neerajdas.com/ux/</link><pubDate>Tue, 21 Feb 2023 00:00:00 +0000</pubDate><guid>https://neerajdas.com/ux/</guid><description>&lt;div class="blog-container theme-bg-surface rounded-5 py-5" style="--bs-bg-opacity: 0.6; --bs-border-radius-2xl: 3rem;">
&lt;div class="container">
&lt;div class="row">
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;div class="d-flex align-items-center justify-content-center justify-content-md-start mb-4">
&lt;span class="theme-text-primary" style="font-family: 'Lemonada', cursive; font-size: 2rem;">Foodies&lt;/span>
&lt;/div>
&lt;h4 class="theme-text-primary text-center text-md-start mb-4">
Food delivery mobile app.
&lt;/h4>
&lt;div class="d-flex justify-content-center justify-content-md-start flex-wrap gap-2">
&lt;a href="https://neerajdas.com/ux/foodies"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
View case study
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;a href="https://www.figma.com/proto/W6EntDiwAN7SqHDTkR2mUZ/Foodies?node-id=238%3A1009&amp;scaling=scale-down&amp;page-id=238%3A1008&amp;starting-point-node-id=238%3A1009"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Play prototype
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;/div>
&lt;/div>
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;div class="d-flex justify-content-center">
&lt;img src="https://neerajdas.com/assets/images/foodies/foodies.png" class="img-fluid" alt="Foodies image"
style="max-width: 1200px; width: 100%;">
&lt;/div>
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;div class="row">
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;div class="d-flex align-items-center justify-content-center justify-content-md-start mb-4">
&lt;span class="theme-text-primary" style="font-family: 'Lemonada', cursive; font-size: 2rem;">Foodies&lt;/span>
&lt;/div>
&lt;h4 class="theme-text-primary text-center text-md-start mb-4">
Food delivery responsive web app.
&lt;/h4>
&lt;div class="d-flex justify-content-center justify-content-md-start flex-wrap gap-2">
&lt;a href="https://neerajdas.com/ux/foodies-web"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
View case study
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;a href="https://www.figma.com/proto/W6EntDiwAN7SqHDTkR2mUZ/Foodies?node-id=545%3A1857&amp;scaling=scale-down&amp;page-id=545%3A1856&amp;starting-point-node-id=545%3A1857"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Play prototype
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;/div>
&lt;/div>
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;img src="https://neerajdas.com/assets/images/foodies_desktop/foodies_desktop.png" class="img-fluid" alt="Foodies desktop image"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;div class="row">
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;div class="d-flex align-items-center justify-content-center justify-content-md-start mb-4">
&lt;span class="theme-text-primary" style="font-size: 2rem;">Blood Donor&lt;/span>
&lt;/div>
&lt;h4 class="theme-text-primary text-center text-md-start mb-4">
Mobile app to help people donate or find blood donors.
&lt;/h4>
&lt;div class="d-flex justify-content-center justify-content-md-start flex-wrap gap-2">
&lt;a href="https://neerajdas.com/ux/blood-donor"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
View case study
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;a href="https://www.figma.com/proto/XCwGka9bxhZJg4w2ALasZs/Blood-Donor?node-id=24%3A200&amp;scaling=scale-down&amp;page-id=0%3A1&amp;starting-point-node-id=24%3A200"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Play prototype
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;/div>
&lt;/div>
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;img src="https://neerajdas.com/assets/images/blood_donor/blood_donor.png" class="img-fluid" alt="Blood donor app screenshot"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div></description></item><item><title>Web development</title><link>https://neerajdas.com/dev/</link><pubDate>Tue, 21 Feb 2023 00:00:00 +0000</pubDate><guid>https://neerajdas.com/dev/</guid><description>&lt;div class="blog-container theme-bg-surface rounded-5 py-5" style="--bs-bg-opacity: 0.6; --bs-border-radius-2xl: 3rem;">
&lt;div class="container">
&lt;div class="row">
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;div class="d-flex align-items-center justify-content-center justify-content-md-start mb-4">
&lt;img src="https://materialstyle.github.io/assets/images/MSIconNewColorV2.svg"
alt="Material Style Logo" style="width:40px; height:40px;">
&lt;span class="theme-text-primary ms-2" style="font-family: 'Roboto Mono', monospace; font-size: 1.8rem;">MATERIAL STYLE&lt;/span>
&lt;/div>
&lt;h4 class="theme-text-primary text-center text-md-start mb-4">
A Material Design UI Library based on Bootstrap.
&lt;/h4>
&lt;div class="d-flex justify-content-center justify-content-md-start flex-wrap gap-2">
&lt;a href="https://neerajdas.com/blog/material-style"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Read full blog
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;a href="https://materialstyle.github.io/"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Visit Material Style
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;/div>
&lt;/div>
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;img src="https://neerajdas.com/assets/images/materialstyle.png" class="img-fluid shadow-2dp" alt="Material Style website screenshot"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;div class="row">
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;div class="d-flex align-items-center justify-content-center justify-content-md-start mb-4">
&lt;span class="theme-text-primary" style="font-family: 'Kaushan Script', cursive; font-size: 2.5rem;">CueSync&lt;/span>
&lt;/div>
&lt;h4 class="theme-text-primary text-center text-md-start mb-4">
A JavaScript library designed to simplify the integration of interactive transcripts into multimedia content.
&lt;/h4>
&lt;div class="d-flex justify-content-center justify-content-md-start flex-wrap gap-2">
&lt;a href="https://cuesync.github.io/"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Visit CueSync
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;/div>
&lt;/div>
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;img src="https://neerajdas.com/assets/images/cuesync.png" class="img-fluid shadow-2dp" alt="CueSync website screenshot"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;/div>
&lt;svg class="squiggle my-5" aria-hidden="true" width="100%" height="8" fill="none" xmlns="http://www.w3.org/2000/svg">
&lt;pattern id="s1" width="91" height="8" patternUnits="userSpaceOnUse">
&lt;g>
&lt;path d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0" stroke="var(--bs-purple)" stroke-linecap="square">&lt;/path>
&lt;/g>
&lt;/pattern>
&lt;rect width="100%" height="100%" fill="url(#s1)">&lt;/rect>
&lt;/svg>
&lt;div class="row">
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;div class="d-flex align-items-center justify-content-center justify-content-md-start mb-4">
&lt;span class="theme-text-primary" style="font-family: 'Kaushan Script', cursive; font-size: 2.5rem;">Marqueefy&lt;/span>
&lt;/div>
&lt;h4 class="theme-text-primary text-center text-md-start mb-4">
A custom Marquee component used to create horizontal or vertical scrolling content.
&lt;/h4>
&lt;div class="d-flex justify-content-center justify-content-md-start flex-wrap gap-2">
&lt;a href="https://marqueefy.github.io/"
class="btn btn-purple rounded-pill" role="button" style="width:250px;">
Visit Marqueefy
&lt;span class="ripple-surface">&lt;/span>
&lt;/a>
&lt;/div>
&lt;/div>
&lt;div class="col-md-6 pt-0 pt-md-5 pb-5">
&lt;img src="https://neerajdas.com/assets/images/marqueefy.png" class="img-fluid shadow-2dp" alt="Marqueefy website screenshot"
style="max-width: 1000px; width: 100%;">
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div></description></item><item><title>Locked out of SSH after enabling UFW on Ubuntu EC2 instance?</title><link>https://neerajdas.com/blog/locked-out-of-ssh-after-enabling-ufw/</link><pubDate>Thu, 03 Jun 2021 00:00:00 +0000</pubDate><guid>https://neerajdas.com/blog/locked-out-of-ssh-after-enabling-ufw/</guid><description>&lt;p>Locked out of SSH after enabling UFW on Ubuntu EC2 instance?&lt;/p>
&lt;p>The solution is to stop the instance and instruct it to disable the firewall program upon restart.&lt;/p>
&lt;p>WARNING: Before starting this procedure, review the following:&lt;/p>
&lt;ul>
&lt;li>Stopping and starting the instance erases any data on &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/instance-store-vs-ebs/">instance store volumes&lt;/a>. Be sure that you &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/back-up-instance-store-ebs/">back up any data on the instance store volume&lt;/a> that you want to keep. For more information, see &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ComponentsAMIs.html#display-ami-root-device-type">Determine the root device type of your AMI&lt;/a>.&lt;/li>
&lt;li>Stopping and restarting the instance changes the public IP address of your instance. It&amp;rsquo;s a best practice to use an &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html">Elastic IP address&lt;/a> instead of a public IP address when routing external traffic to your instance.&lt;/li>
&lt;/ul>
&lt;ol>
&lt;li>
&lt;p>Stop your instance. (Do not terminate)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>In the console, select your instance and go to Actions -&amp;gt; Instance Settings -&amp;gt; Edit user data&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Select Modify user data as text and paste the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Content-Type: multipart/mixed; boundary=&amp;#34;//&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MIME-Version: 1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--//
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Content-Type: text/cloud-config; charset=&amp;#34;us-ascii&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MIME-Version: 1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Content-Transfer-Encoding: 7bit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Content-Disposition: attachment; filename=&amp;#34;cloud-config.txt&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#cloud-config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cloud_final_modules:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- [scripts-user, always]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--//
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Content-Type: text/x-shellscript; charset=&amp;#34;us-ascii&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">MIME-Version: 1.0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Content-Transfer-Encoding: 7bit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Content-Disposition: attachment; filename=&amp;#34;userdata.txt&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">#!/bin/bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ufw disable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iptables -L
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">iptables -F
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">--//
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Save and Restart the instance and SSH should work. The user data disables UFW and flushes any iptable rules blocking SSH access.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>After successfully connecting via SSH, you may reset the user data by following previous steps.
Select Modify user data as text, delete all text and save.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>When a user data script is processed, it is copied to and run from &lt;code>/var/lib/cloud/instances/instance-id/&lt;/code>.
The script is not deleted after it is run. Be sure to delete the user data scripts from
&lt;code>/var/lib/cloud/instances/instance-id/&lt;/code> before you create an AMI from the instance.
Otherwise, the script will exist in this directory on any instance launched from the AMI.&lt;/p>
&lt;h2 id="reference">Reference&lt;/h2>
&lt;p>&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html">https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/">https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/&lt;/a>&lt;/p></description></item><item><title>Adding PHPUnit code coverage to SonarQube</title><link>https://neerajdas.com/blog/adding-phpunit-code-coverage-to-sonarqube/</link><pubDate>Mon, 19 Apr 2021 00:00:00 +0000</pubDate><guid>https://neerajdas.com/blog/adding-phpunit-code-coverage-to-sonarqube/</guid><description>&lt;p>Code coverage is a measure used to describe the degree to which the source code of a
program is tested by a particular test suite. A program with high code coverage has
been more thoroughly tested and has a lower chance of containing software bugs than
a program with low code coverage.&lt;/p>
&lt;p>We need Xdebug installed in our system to generate a code coverage report.
Run the following commands (Linux) or check &lt;a href="https://xdebug.org/">Xdebug&lt;/a> installation instruction if the commands don’t work.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sudo apt-get install php-xdebug
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo service apache2 restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="generating-code-coverage">Generating code coverage&lt;/h2>
&lt;p>To generate code coverage, update &lt;code>phpunit.xml&lt;/code> as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;phpunit&lt;/span> &lt;span class="na">xmlns:xsi=&lt;/span>&lt;span class="s">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xsi:noNamespaceSchemaLocation=&lt;/span>&lt;span class="s">&amp;#34;https://schema.phpunit.de/9.3/phpunit.xsd&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">bootstrap=&lt;/span>&lt;span class="s">&amp;#34;vendor/autoload.php&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">cacheResultFile=&lt;/span>&lt;span class="s">&amp;#34;.phpunit.cache/test-results&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">executionOrder=&lt;/span>&lt;span class="s">&amp;#34;depends,defects&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">beStrictAboutOutputDuringTests=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">beStrictAboutTodoAnnotatedTests=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">failOnRisky=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">failOnWarning=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">verbose=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;php&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;server&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;DOCUMENT_ROOT&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;ABSOLUTE_PATH_TO_DOCUMENT_ROOT&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/php&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;coverage&lt;/span> &lt;span class="na">cacheDirectory=&lt;/span>&lt;span class="s">&amp;#34;.phpunit.cache/code-coverage&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;include&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;directory&lt;/span> &lt;span class="na">suffix=&lt;/span>&lt;span class="s">&amp;#34;.php&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>src&lt;span class="nt">&amp;lt;/directory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/include&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;report&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;clover&lt;/span> &lt;span class="na">outputFile=&lt;/span>&lt;span class="s">&amp;#34;report/tests-clover.xml&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;html&lt;/span> &lt;span class="na">outputDirectory=&lt;/span>&lt;span class="s">&amp;#34;report&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/report&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/coverage&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testsuites&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testsuite&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;default&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;directory&lt;/span> &lt;span class="na">suffix=&lt;/span>&lt;span class="s">&amp;#34;Test.php&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>tests&lt;span class="nt">&amp;lt;/directory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/testsuite&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/testsuites&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;junit&lt;/span> &lt;span class="na">outputFile=&lt;/span>&lt;span class="s">&amp;#34;report/tests-junit.xml&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testdoxHtml&lt;/span> &lt;span class="na">outputFile=&lt;/span>&lt;span class="s">&amp;#34;report/testdox.html&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/phpunit&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And run tests:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">./vendor/bin/phpunit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you get a warning: &lt;code>XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set&lt;/code>,
Run the tests as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">XDEBUG_MODE=coverage ./vendor/bin/phpunit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="viewing-coverage-in-a-browser">Viewing coverage in a browser&lt;/h2>
&lt;p>Output directory is set as &lt;code>report&lt;/code>. So, open &lt;code>report/index.html&lt;/code> on a browser to see
the code coverage.&lt;br>
&lt;br/>&lt;/p>
&lt;h2 id="adding-code-coverage-to-sonarqube">Adding Code Coverage to SonarQube&lt;/h2>
&lt;p>SonarQube uses &lt;code>coverage.report.clover&lt;/code> and &lt;code>logging.junit&lt;/code> to show code coverage.&lt;br>
&lt;code>coverage.report.html&lt;/code> generates a code coverage report in HTML.&lt;br>
&lt;code>logging.testdoxHtml&lt;/code> generates an HTML report containing a list of Test Cases written.&lt;br>
&lt;code>outputFile&lt;/code> sets report path.&lt;/p>
&lt;p>Add report paths to sonar properties file&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sonar.tests=tests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sonar.php.tests.reportPath=report/tests-junit.xml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sonar.php.coverage.reportPath=report/tests-clover.xml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>sonar.tests&lt;/code> = path to the test directory of your project&lt;br>
&lt;code>sonar.php.tests.reportPath&lt;/code> = path to junit report&lt;br>
&lt;code>sonar.php.coverage.reportPath&lt;/code> = path to clover report&lt;/p>
&lt;p>Exclude files that are not unit tested&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sonar.test.exclusions=tests/app/**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sonar.coverage.exclusions=tests/app/**
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>sonar.test.exclusions&lt;/code> = files excluded from unit tests&lt;br>
&lt;code>sonar.coverage.exclusions&lt;/code> = files excluded from code coverage&lt;/p>
&lt;p>Run Sonar scanner&lt;/p></description></item><item><title>Getting started with PHPUnit</title><link>https://neerajdas.com/blog/getting-started-with-phpunit/</link><pubDate>Tue, 18 Aug 2020 00:00:00 +0000</pubDate><guid>https://neerajdas.com/blog/getting-started-with-phpunit/</guid><description>&lt;p>A unit test provides a strict, written contract that a piece of code must satisfy.
As a result, unit tests find problems early in the development cycle.&lt;/p>
&lt;p>The goal is to isolate each part of the program and verify that it is correct.&lt;/p>
&lt;p>&lt;a href="https://phpunit.de/">PHPUnit&lt;/a> is a well-known testing framework for PHP. It uses assertions to verify that a specific
component or unit behaves as expected.&lt;/p>
&lt;p>The purpose of this tutorial is to introduce you to the basics of PHPUnit.&lt;br>
&lt;br/>&lt;/p>
&lt;h2 id="installation">Installation&lt;/h2>
&lt;p>Before we start writing our first unit test, we need to have PHPUnit installed. The installation
process is documented at &lt;a href="https://phpunit.de/">https://phpunit.de/&lt;/a>.&lt;br>
&lt;br/>&lt;/p>
&lt;h2 id="writing-our-first-test">Writing our first test&lt;/h2>
&lt;div class="p-3 my-3 author-note">
Before we begin,&lt;br>
Please Clone / Download the example code from &lt;a target="_blank" href="https://github.com/nkdas91/Getting-started-with-PHPUnit">GitHub&lt;/a>.&lt;br>
Copy &lt;b>env.example.php&lt;/b> to &lt;b>env.php&lt;/b> and replace default values.&lt;br>
Create a DB &lt;b>phpunit&lt;/b> and import &lt;b>sql/phpunit.sql&lt;/b>.&lt;br>
Update &lt;b>ABSOLUTE PATH TO DOCUMENT ROOT&lt;/b> in &lt;b>phpunit.xml&lt;/b>.&lt;br>
Run &lt;b>composer install&lt;/b> in the root directory.
&lt;/div>
&lt;p>To get started, we need something to test, so for the first example, I’ve written a simple
PHP class &lt;code>Average&lt;/code> that calculates the average of an array of integers.&lt;/p>
&lt;p>src/Average.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="nx">php&lt;/span> &lt;span class="k">declare&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">strict_types&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Average&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">ensureIsValidArrayOfIntegers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">array&lt;/span> &lt;span class="nv">$numbers&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$numbers&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nv">$number&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">filter_var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$number&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">FILTER_VALIDATE_INT&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">InvalidArgumentException&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">sprintf&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;&amp;#34;%s&amp;#34; is not a valid number&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$number&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">getAverage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">array&lt;/span> &lt;span class="nv">$numbers&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">float&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">ensureIsValidArrayOfIntegers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$numbers&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">array_sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$numbers&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$numbers&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Basic coventions for writing tests with PHPUnit:&lt;/p>
&lt;ol>
&lt;li>The tests for a class &lt;code>Class&lt;/code> go into a class &lt;code>ClassTest&lt;/code>.&lt;/li>
&lt;li>&lt;code>ClassTest&lt;/code> inherits (most of the time) from &lt;code>PHPUnit\Framework\TestCase&lt;/code>.&lt;/li>
&lt;li>The tests are public methods that are named &lt;code>test*&lt;/code>.&lt;/li>
&lt;li>Alternatively, we can use the &lt;code>@test&lt;/code> annotation in a method’s docblock to mark it as a test method.&lt;/li>
&lt;li>Inside the test methods, assertion methods are used to assert that an actual value matches an expected value.&lt;/li>
&lt;/ol>
&lt;p>To ensure that our class &lt;code>Average&lt;/code> works, we need to create a test class &lt;code>AverageTest&lt;/code> that extends &lt;code>PHPUnit\Framework\TestCase&lt;/code>.&lt;/p>
&lt;p>tests/AverageTest.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="nx">php&lt;/span> &lt;span class="k">declare&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">strict_types&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">use&lt;/span> &lt;span class="nx">PHPUnit\Framework\TestCase&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">final&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">AverageTest&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="nx">TestCase&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Test for an Exception if invalid argument type is passed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testExceptionFromInvalidArgumentType&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">expectException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">TypeError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$average&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Average&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$verage&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;string&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Test for an Exception if invalid argument is passed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testExceptionFromInvalidArgument&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">expectException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">InvalidArgumentException&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$average&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Average&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Test for an Exception if an empty array is passed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testExceptionFromEmptyArrayArgument&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">expectException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">DivisionByZeroError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$average&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Average&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testGetAverage&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$average&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Average&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">assertEquals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">3.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">]));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="running-our-tests">Running our tests&lt;/h2>
&lt;p>Running our tests is as simple as calling the phpunit executable and pointing it at our tests.
Here’s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">./vendor/bin/phpunit tests
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">PHPUnit 9.5.0 by Sebastian Bergmann and contributors.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.... 4 / 4 (100%)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Time: 00:00.008, Memory: 4.00 MB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OK (4 tests, 4 assertions)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="fixtures-setup--teardown">Fixtures (Setup &amp;amp; Teardown)&lt;/h2>
&lt;p>The purpose of a fixture is to ensure that there is a well known and fixed environment
in which tests are run. This allows for tests to be repeatable, which is one of the key
features of an effective test framework.&lt;/p>
&lt;p>Examples:&lt;/p>
&lt;ul>
&lt;li>Loading a database with a specific known set of data.&lt;/li>
&lt;li>Preparation of input data as well as set-up and creation of mock objects.&lt;/li>
&lt;li>Copying a specific known set of files&lt;/li>
&lt;/ul>
&lt;p>PHPUnit supports sharing the setup code. Before a test method is run, a template
method called &lt;code>setUp()&lt;/code> is invoked. &lt;code>setUp()&lt;/code> is where we create the objects against
which we will test. Once the test method has finished running, whether it succeeded
or failed, another template method called &lt;code>tearDown()&lt;/code> is invoked. &lt;code>tearDown()&lt;/code> is where
we clean up the objects against which we tested.&lt;/p>
&lt;p>In &lt;code>AverageTest.php&lt;/code>, it is tedious to instantiate &lt;code>Average&lt;/code> class in each test case.
So, we move it to &lt;code>setUp()&lt;/code> and &lt;code>tearDown()&lt;/code>.&lt;/p>
&lt;div class="p-3 my-3 author-note">Also check tests/UserTest.php for another example.&lt;/div>
&lt;p>tests/AverageTest.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="nx">php&lt;/span> &lt;span class="k">declare&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">strict_types&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">use&lt;/span> &lt;span class="nx">PHPUnit\Framework\TestCase&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">final&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">AverageTest&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="nx">TestCase&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">protected&lt;/span> &lt;span class="nv">$average&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * This function is invoked before each test function.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">protected&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">setUp&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Average&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * This function is invoked after each test function.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">protected&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">tearDown&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">unset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Test for an Exception if invalid argument type is passed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testExceptionFromInvalidArgumentType&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">expectException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">TypeError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;string&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Test for an Exception if invalid argument is passed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testExceptionFromInvalidArgument&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">expectException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">InvalidArgumentException&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;a&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Test for an Exception if an empty array is passed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testExceptionFromEmptyArrayArgument&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">expectException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">DivisionByZeroError&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testGetAverage&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">assertEquals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">3.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">]));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="data-providers">Data Providers&lt;/h2>
&lt;p>A test method can accept arbitrary arguments. These arguments are to be provided by one or
more data provider methods. The data provider method to be used is specified using the
&lt;code>@dataProvider&lt;/code> annotation.&lt;/p>
&lt;p>tests/AverageTest.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * @dataProvider averageProvider
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testGetAverageUsingDataProvider&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">int&lt;/span> &lt;span class="nv">$a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">int&lt;/span> &lt;span class="nv">$b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">float&lt;/span> &lt;span class="nv">$expected&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">assertSame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$expected&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nv">$a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$b&lt;/span>&lt;span class="p">]));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">averageProvider&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">array&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">1.5&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.0&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">4.0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="test-doubles">Test Doubles&lt;/h2>
&lt;p>When we are writing a test in which we cannot (or chose not to) use a real depended-on
component (DOC), we can replace it with a Test Double. The Test Double doesn’t have to
behave exactly like the real DOC; it merely has to provide the same API as the real one
so that the system under test (SUT) thinks it is the real one!&lt;/p>
&lt;p>The &lt;code>createStub&lt;/code> and &lt;code>createMock&lt;/code> methods can be used in a test to
automatically generate an object that can act as a test double.&lt;/p>
&lt;p>By default, all methods of the original class are replaced with a dummy implementation
that returns null (without calling the original method). We can configure these dummy
implementations to return a value when called Using the &lt;code>will($this-&amp;gt;returnValue())&lt;/code>
or simply &lt;code>willReturn&lt;/code> method.&lt;/p>
&lt;p>When the defaults used by the &lt;code>createStub&lt;/code> and &lt;code>createMock&lt;/code> methods do not match
our needs then we can use the &lt;code>getMockBuilder&lt;/code> method to customize the test double
generation.&lt;/p>
&lt;p>Please note that &lt;code>final&lt;/code>, &lt;code>private&lt;/code>, and &lt;code>static&lt;/code> methods cannot be stubbed or mocked.&lt;/p>
&lt;h3 id="stubs">Stubs&lt;/h3>
&lt;p>The practice of replacing an object with a test double that (optionally) returns configured
return values is referred to as stubbing. You can use a stub to replace a real component
on which the SUT depends so that the test has a control point for the indirect inputs of the SUT.&lt;/p>
&lt;p>Let&amp;rsquo;s stub &lt;code>ensureIsValidArrayOfIntegers&lt;/code> method and skip validation.&lt;/p>
&lt;p>tests/AverageTest.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testStub&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * createStub stubs all the methods in the stubbed class.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Here, ensureIsValidArrayOfIntegers and getAverage methods
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * won&amp;#39;t be invoked from the Original Average class.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$stub&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">createStub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Average&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Since we didn&amp;#39;t specify the return values,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * default values will be returned based on the functions return type.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Return type of ensureIsValidArrayOfIntegers is void. So, nothing is returned.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Return type of getAverage is float. So, 0.0 is returned
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">assertEmpty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$stub&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">ensureIsValidArrayOfIntegers&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">]));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">assertEquals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">round&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$stub&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">]),&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>tests/AverageTest.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testStubUsingMockBuilder&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Here we used getMockBuilder to stub just one method: ensureIsValidArrayOfIntegers.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * This method won&amp;#39;t be invoked.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$stub&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getMockBuilder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Average&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">setMethods&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="s1">&amp;#39;ensureIsValidArrayOfIntegers&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getMock&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">assertEmpty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$stub&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">ensureIsValidArrayOfIntegers&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">]));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Since, ensureIsValidArrayOfIntegers method is stubbed,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * we are able to get an average even if we don&amp;#39;t pass a valid array of Integers.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * getAverage method of the original Average class is invoked.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">assertEquals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">2.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">round&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$stub&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">getAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">3.5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">]),&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br>
&lt;h3 id="mock-objects">Mock Objects&lt;/h3>
&lt;p>The practice of replacing an object with a test double that verifies expectations, for instance
asserting that a method has been called, is referred to as mocking.&lt;/p>
&lt;p>Let&amp;rsquo;s create a class &lt;code>Logger&lt;/code> that logs a string.&lt;/p>
&lt;p>src/Logger.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;?&lt;/span>&lt;span class="nx">php&lt;/span> &lt;span class="k">declare&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">strict_types&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Logger&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$text&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">echo&lt;/span> &lt;span class="nv">$text&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We will add a method to the &lt;code>Average&lt;/code> class that uses Logger&amp;rsquo;s log method to log average.&lt;/p>
&lt;p>src/Average.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">logAverage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">array&lt;/span> &lt;span class="nv">$numbers&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">Logger&lt;/span> &lt;span class="nv">$logger&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">ensureIsValidArrayOfIntegers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$numbers&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$logger&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">array_sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$numbers&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nx">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$numbers&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now let&amp;rsquo;s Mock &lt;code>Logger&lt;/code> and test if &lt;code>log&lt;/code> method is called.&lt;/p>
&lt;p>tests/AverageTest.php&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-php" data-lang="php">&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span> &lt;span class="k">function&lt;/span> &lt;span class="nf">testMock&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sd">/**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * Here, we are mocking log method of Logger class,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * just to ensure that it is called with the specified arguments.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> * The Average class does not need to verify what happens within the Logger log method.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$mockObject&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">createMock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">Logger&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$mockObject&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">expects&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">once&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">method&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;log&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">2.0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">$this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">average&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="na">logAverage&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nv">$mockObject&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="xml-configuration">XML Configuration&lt;/h2>
&lt;p>&lt;code>phpunit.xml&lt;/code> file can be used to compose a test suite and specify other configurations.
The following is an xml configuration that will add all &lt;code>*Test&lt;/code> classes that are
found in &lt;code>*Test.php&lt;/code> files when the &lt;code>tests&lt;/code> directory is recursively traversed.&lt;/p>
&lt;p>phpunit.xml&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;phpunit&lt;/span> &lt;span class="na">xmlns:xsi=&lt;/span>&lt;span class="s">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xsi:noNamespaceSchemaLocation=&lt;/span>&lt;span class="s">&amp;#34;https://schema.phpunit.de/9.3/phpunit.xsd&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">bootstrap=&lt;/span>&lt;span class="s">&amp;#34;vendor/autoload.php&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">cacheResultFile=&lt;/span>&lt;span class="s">&amp;#34;.phpunit.cache/test-results&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">executionOrder=&lt;/span>&lt;span class="s">&amp;#34;depends,defects&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">beStrictAboutOutputDuringTests=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">beStrictAboutTodoAnnotatedTests=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">failOnRisky=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">failOnWarning=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">verbose=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;php&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;server&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;DOCUMENT_ROOT&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;ABSOLUTE_PATH_TO_DOCUMENT_ROOT&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/php&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testsuites&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testsuite&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;default&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;directory&lt;/span> &lt;span class="na">suffix=&lt;/span>&lt;span class="s">&amp;#34;Test.php&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>tests&lt;span class="nt">&amp;lt;/directory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/testsuite&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/testsuites&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/phpunit&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>running our tests&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">./vendor/bin/phpunit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;br/>
&lt;h2 id="code-coverage">Code Coverage&lt;/h2>
&lt;p>Code coverage is a measure used to describe the degree to which the source code of a
program is tested by a particular test suite. A program with high code coverage has
been more thoroughly tested and has a lower chance of containing software bugs than
a program with low code coverage.&lt;/p>
&lt;p>To generate code coverage, update &lt;code>phpunit.xml&lt;/code> as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;phpunit&lt;/span> &lt;span class="na">xmlns:xsi=&lt;/span>&lt;span class="s">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xsi:noNamespaceSchemaLocation=&lt;/span>&lt;span class="s">&amp;#34;https://schema.phpunit.de/9.3/phpunit.xsd&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">bootstrap=&lt;/span>&lt;span class="s">&amp;#34;vendor/autoload.php&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">cacheResultFile=&lt;/span>&lt;span class="s">&amp;#34;.phpunit.cache/test-results&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">executionOrder=&lt;/span>&lt;span class="s">&amp;#34;depends,defects&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">beStrictAboutOutputDuringTests=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">beStrictAboutTodoAnnotatedTests=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">failOnRisky=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">failOnWarning=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">verbose=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;php&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;server&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;DOCUMENT_ROOT&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;ABSOLUTE_PATH_TO_DOCUMENT_ROOT&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/php&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;coverage&lt;/span> &lt;span class="na">cacheDirectory=&lt;/span>&lt;span class="s">&amp;#34;.phpunit.cache/code-coverage&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;include&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;directory&lt;/span> &lt;span class="na">suffix=&lt;/span>&lt;span class="s">&amp;#34;.php&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>src&lt;span class="nt">&amp;lt;/directory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/include&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;report&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;clover&lt;/span> &lt;span class="na">outputFile=&lt;/span>&lt;span class="s">&amp;#34;report/tests-clover.xml&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;html&lt;/span> &lt;span class="na">outputDirectory=&lt;/span>&lt;span class="s">&amp;#34;report&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/report&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/coverage&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testsuites&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testsuite&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;default&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;directory&lt;/span> &lt;span class="na">suffix=&lt;/span>&lt;span class="s">&amp;#34;Test.php&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>tests&lt;span class="nt">&amp;lt;/directory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/testsuite&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/testsuites&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;junit&lt;/span> &lt;span class="na">outputFile=&lt;/span>&lt;span class="s">&amp;#34;report/tests-junit.xml&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;testdoxHtml&lt;/span> &lt;span class="na">outputFile=&lt;/span>&lt;span class="s">&amp;#34;report/testdox.html&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/phpunit&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And run tests:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">./vendor/bin/phpunit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you get a warning: &lt;code>XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set&lt;/code>,
Run the tests as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">XDEBUG_MODE=coverage ./vendor/bin/phpunit
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output directory is set as &lt;code>report&lt;/code>. So, open &lt;code>report/index.html&lt;/code> on a browser to see
the code coverage.&lt;/p>
&lt;p>I hope this is a good introduction to the world of unit testing. Even though there are several
topics I’ve not touched on, I’ve tried to give you a good point where you can start writing
your tests.&lt;/p></description></item><item><title>Material Style - A UI Library</title><link>https://neerajdas.com/blog/material-style/</link><pubDate>Tue, 08 Jan 2019 00:00:00 +0000</pubDate><guid>https://neerajdas.com/blog/material-style/</guid><description>&lt;p>A year ago, I was involved in a project with the requirement to have Material Design styles and animations for the UI.
Like always, I looked for frameworks or libraries that met the design specification.&lt;/p>
&lt;p>The first library that I came across was &lt;a href="https://getmdl.io/">Material Design Lite&lt;/a>.&lt;br>
It is a super lightweight library that didn’t rely on any JavaScript frameworks.
Unfortunately, it didn’t have essential components such as Select Boxes.
Having found it inadequate, I moved on for the next.&lt;/p>
&lt;p>&lt;a href="https://mdbootstrap.com/">MD Bootstrap&lt;/a>&lt;br>
It was a popular library built on top of Bootstrap. I was curious to see if it could fulfil the requirement.
Once again, I was left disappointed because the styles and animations were not as per the Material Design Guidelines.
Moreover, we could not use all its components unless we purchased the PRO version of it.&lt;/p>
&lt;p>&lt;a href="https://materializecss.com/">Materialize&lt;/a>&lt;br>
Developed by a team of students from Carnegie Mellon University, it was a complete framework that didn’t depend on
Bootstrap or jQuery. But, like MD Bootstrap, the animations were not as per the Material Design Guidelines.&lt;/p>
&lt;p>Thus, I still did not have any luck going my way with finding a suitable Material Design package.&lt;/p>
&lt;p>And then I decided to use Bootstrap as it is and write my CSS and JS on top of it to fulfil the project’s requirement.
It gave me an idea to create a UI wrapper which would have all the bootstrap components, look and behave as per the
Material Design Guidelines. I set about developing it and six months later, with some hard work,
research and sleepless nights, &lt;a href="https://materialstyle.github.io/">Material Style&lt;/a> was born.&lt;/p>
&lt;p>&lt;a href="https://materialstyle.github.io/">Material Style&lt;/a> is now an easy to use UI Library based on Bootstrap 4.5 that lets you add Material Design styles
and animations to Bootstrap components.&lt;/p>
&lt;p>&lt;a href="https://materialstyle.github.io/"
class="btn btn-purple rounded-pill" role="button" style="width:200px;">
Visit Material Style
&lt;/a>&lt;/p>
&lt;h2 id="few-components-from-material-style">Few Components from Material Style&lt;/h2>
&lt;div class="row p-4 material-style-showcase" id="example-light">
&lt;div class="col">
&lt;button type="button" class="btn btn-purple btn-lg m-1">
Button
&lt;span class="ripple-surface">&lt;/span>
&lt;/button>
&lt;button type="button" class="btn btn-purple btn-lg rounded-pill m-1">
&lt;i class="bi bi-search">&lt;/i> Search
&lt;span class="ripple-surface">&lt;/span>
&lt;/button>
&lt;button type="button" class="btn btn-fab btn-purple m-1">
&lt;i class="bi bi-heart-fill">&lt;/i>
&lt;span class="ripple-surface">&lt;/span>
&lt;/button>
&lt;span class="d-block">&lt;/span>
&lt;fieldset class="form-floating base-purple primary-green m-3 bg-trans" style="max-width: 200px;">
&lt;input type="text" class="form-control" id="firstname"
placeholder="firstname" autocomplete="off">
&lt;label for="firstname">Firstname&lt;/label>
&lt;/fieldset>
&lt;fieldset class="form-floating form-floating-outlined base-purple primary-green m-3" style="max-width: 200px;">
&lt;input type="text" class="form-control" id="firstname-outline"
placeholder="firstname" autocomplete="off">
&lt;label for="firstname-outline">Firstname&lt;/label>
&lt;/fieldset>
&lt;fieldset class="form-floating base-purple primary-green m-3" style="max-width: 200px;">
&lt;select class="form-select">
&lt;option value="">&lt;/option>
&lt;option value="1">Option 1&lt;/option>
&lt;option value="2">Option 2&lt;/option>
&lt;option value="3">Option 3&lt;/option>
&lt;option value="4">Option 4&lt;/option>
&lt;/select>
&lt;label>Select One&lt;/label>
&lt;/fieldset>
&lt;fieldset class="form-floating form-floating-outlined base-purple primary-green m-3" style="max-width: 200px;">
&lt;select class="form-select">
&lt;option value="">&lt;/option>
&lt;option value="1">Option 1&lt;/option>
&lt;option value="2">Option 2&lt;/option>
&lt;option value="3">Option 3&lt;/option>
&lt;option value="4">Option 4&lt;/option>
&lt;/select>
&lt;label>Select One&lt;/label>
&lt;/fieldset>
&lt;/div>
&lt;/div></description></item></channel></rss>