Search by Algolia
What is online retail merchandising? An introduction
e-commerce

What is online retail merchandising? An introduction

Done any shopping on an ecommerce website lately? If so, you know a smooth online shopper experience is not optional ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

5 considerations for Black Friday 2023 readiness
e-commerce

5 considerations for Black Friday 2023 readiness

It’s hard to imagine having to think about Black Friday less than 4 months out from the previous one ...

Piyush Patel

Chief Strategic Business Development Officer

How to increase your sales and ROI with optimized ecommerce merchandising
e-commerce

How to increase your sales and ROI with optimized ecommerce merchandising

What happens if an online shopper arrives on your ecommerce site and: Your navigation provides no obvious or helpful direction ...

Catherine Dee

Search and Discovery writer

Mobile search UX best practices, part 3: Optimizing display of search results
ux

Mobile search UX best practices, part 3: Optimizing display of search results

In part 1 of this blog-post series, we looked at app interface design obstacles in the mobile search experience ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

Mobile search UX best practices, part 2: Streamlining search functionality
ux

Mobile search UX best practices, part 2: Streamlining search functionality

In part 1 of this series on mobile UX design, we talked about how designing a successful search user experience ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

Mobile search UX best practices, part 1: Understanding the challenges
ux

Mobile search UX best practices, part 1: Understanding the challenges

Welcome to our three-part series on creating winning search UX design for your mobile app! This post identifies developer ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

Teaching English with Zapier and Algolia
engineering

Teaching English with Zapier and Algolia

National No Code Day falls on March 11th in the United States to encourage more people to build things online ...

Alita Leite da Silva

How AI search enables ecommerce companies to boost revenue and cut costs
ai

How AI search enables ecommerce companies to boost revenue and cut costs

Consulting powerhouse McKinsey is bullish on AI. Their forecasting estimates that AI could add around 16 percent to global GDP ...

Michelle Adams

Chief Revenue Officer at Algolia

What is digital product merchandising?
e-commerce

What is digital product merchandising?

How do you sell a product when your customers can’t assess it in person: pick it up, feel what ...

Catherine Dee

Search and Discovery writer

Scaling marketplace search with AI
ai

Scaling marketplace search with AI

It is clear that for online businesses and especially for Marketplaces, content discovery can be especially challenging due to the ...

Bharat Guruprakash

Chief Product Officer

The changing face of digital merchandising
e-commerce

The changing face of digital merchandising

This 2-part feature dives into the transformational journey made by digital merchandising to drive positive ecommerce experiences. Part 1 ...

Reshma Iyer

Director of Product Marketing, Ecommerce

What’s a convolutional neural network and how is it used for image recognition in search?
ai

What’s a convolutional neural network and how is it used for image recognition in search?

A social media user is shown snapshots of people he may know based on face-recognition technology and asked if ...

Catherine Dee

Search and Discovery writer

What’s organizational knowledge and how can you make it accessible to the right people?
product

What’s organizational knowledge and how can you make it accessible to the right people?

How’s your company’s organizational knowledge holding up? In other words, if an employee were to leave, would they ...

Catherine Dee

Search and Discovery writer

Adding trending recommendations to your existing e-commerce store
engineering

Adding trending recommendations to your existing e-commerce store

Recommendations can make or break an online shopping experience. In a world full of endless choices and infinite scrolling, recommendations ...

Ashley Huynh

Ecommerce trends for 2023: Personalization
e-commerce

Ecommerce trends for 2023: Personalization

Algolia sponsored the 2023 Ecommerce Site Search Trends report which was produced and written by Coleman Parkes Research. The report ...

Piyush Patel

Chief Strategic Business Development Officer

10 ways to know it’s fake AI search
ai

10 ways to know it’s fake AI search

You think your search engine really is powered by AI? Well maybe it is… or maybe not.  Here’s a ...

Michelle Adams

Chief Revenue Officer at Algolia

Cosine similarity: what is it and how does it enable effective (and profitable) recommendations?
ai

Cosine similarity: what is it and how does it enable effective (and profitable) recommendations?

You looked at this scarf twice; need matching mittens? How about an expensive down vest? You watched this goofy flick ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

What is cognitive search, and what could it mean for your business?
ai

What is cognitive search, and what could it mean for your business?

“I can’t find it.”  Sadly, this conclusion is often still part of the modern enterprise search experience. But ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

Looking for something?

How to build a simulator for any feature — Part 1: Unit testing
facebookfacebooklinkedinlinkedintwittertwittermailmail

A while back, a tweet came up on my timeline that asked the Twitterverse to scare a product manager in three words. Among the wonderful responses were “one more thing…”, “pretty much done”, “that’s legacy code”, and the perhaps underrated “I tested it”. That last one stuck with me because it’s perhaps the scariest of them all.

It leads to so many more questions: how did you test it? Did you cover all of the edge cases? Was the test accurate to how the piece will actually be used? Did the test only check the expected outcome? Did the test cover if the change may have broken something else in the application? Thankfully, us developers have come up with a good system to answer these questions systematically and thoroughly, so let’s explore the two major types of automated testing in this article.

Unit tests

Before we get started building unit tests, it’s good to take a step back and ask why we need them. In general, we want to make sure that our apps are working! Unit tests are just little pieces of code that verify that things are functioning as they should. To understand how unit tests do this, though, it’s important to first realize that modern development involves modularity (splitting functionality into the smallest pieces possible). Once we chunk up our large tasks into these small functions, many of them will happen to be pure functions: deterministic functions that always return the same output given the same input, without affecting or being affected by anything outside of the function.

Those properties make pure functions straightforward to test, since we can just simulate how those functions are going to be used and check the output against what it’s supposed to be. This is what we mean by “unit test”: using the function we want to test a couple of times with fake data and making sure that we get the correct results. For example, take the famous Sigmoid function, written in Python, with a simple unit test beneath it:

import math

def sigmoid(x):
	return 1 / (1 + math.exp(-x))

def test_sigmoid():
	assert sigmoid(0) == 0.5
	assert 0.9999 < sigmoid(10) < 1
	assert 0.2689 < sigmoid(-1) < 0.269
	assert sigmoid(-100000000000000000) == 0

Sure enough, that last line reveals a bug in our code. Who would have thought that the math library in Python couldn’t handle numbers like -100000000000000000? In hindsight, it’s obvious to me — the positive version we’re feeding into math.exp is slightly outside the range of a double, but I didn’t think about that before! This little bug demonstrates well that we can’t know all of the things that there are to know about our tools. Even though the formula here is technically correct, the tool that I’m using can’t handle the edge case of manipulating such a large number. Thankfully, math.exp will handle this fine if we give it a negative number (which is a positive input into sigmoid, as we give math.exp with -x) because the result just rounds to 0, but in the case that x is positive, we need to write the function slightly differently:

def sigmoid(x):
	if x >= 0:
		return 1 / (1 + math.exp(-x))
	else:
		ex = math.exp(x)
		return ex / (1 + ex)

Now, our unit test returns without setting off any alarms! And if our PM happens to think of an edge case we didn’t, it’s trivial to add another assert to the testing function.

What if our function to test isn’t pure, though? Here’s one idea: refactor whatever you can into a new pure function so that the impure function contains as little as possible. For example, take this JavaScript function, which gets a string value from the DOM before running the Caesar cipher on it:

const runCaesarCipherOnInput = () => {
	const input = document.getElementById("input");
	const result = document.getElementById("result");
	const key = 4;
	
	result.innerText = input
		.value
		.toUpperCase()
		.split("")
		.map((letter, i) => {
	    const code = input.charCodeAt(i);
	    if (65 <= code && code <= 90) {
				let n = code - 65 + key;
				if (n < 0) n = 26 - Math.abs(n) % 26;
				return String.fromCharCode((n % 26) + 65);
	    } else return letter;
		})
		.join("");
};

This works! But it’s difficult to unit test because of all that’s going on. It’s not deterministic (that is, the function doesn’t always return the same output for a given input), and it contains a few side effects that reach outside of the scope of the function. Even the internal loop function inside the .map call isn’t pure! How can we fix this?

Let’s try to pull everything making the function impure out for now, leaving us with a few bits of easily-testable functionality, and then we’ll rebuild the pieces into the original function again.

const caesar = (msg, key, rangeStart=65, rangeEnd=91) => msg
	.split("")
	.map(letter => shiftLetter(letter, key, rangeStart, rangeEnd))
	.join("");

const shiftLetter = (letter, key, rangeStart, rangeEnd) => {
	const rangeLength = rangeEnd - rangeStart;
	const code = letter.charCodeAt(0);
	if (rangeStart <= code && code < rangeEnd) {
		let n = code - rangeStart + key;
		if (n < 0) n = rangeLength - Math.abs(n) % rangeLength;
		return String.fromCharCode((n % rangeLength) + rangeStart);
	} else return letter;
};

const runCaesarCipherOnInput = () => {
	const input = document.getElementById("input");
	const result = document.getElementById("result");
	const key = 4;

	result.innerText = caesar(input.value.toUpperCase(), key);
};

Notice in this example, we still have two impure functions: the function fed to .map on line 3 and runCaesarCipherOnInput. However, these functions are simple enough that they don’t need to be tested! They aren’t actually doing any logic (they both primarily exist to feed pure functions arguments), and the constant declarations and DOM reads are fairly straightforward to understand, so we can safely just expect that they’ll do what we want. I could have gone even further if I used a for loop instead of map, but the point here is that creating unit tests often won’t require a big change from you as the developer. This minor rewrite accomplishes the same purpose, it’s easier to read, and it’s now testable — the important logic bits like caesar and shiftLetter are pure functions, so we can just write assertion tests for them like before:

const testCaesar = () => {
	if (caesar("HELLO", 1) != "IFMMP") throw "Assertion error"; // test basic case
	if (caesar("these are all lowercase letters, so nothing should happen", 8) != "these are all lowercase letters, so nothing should happen") throw "Assertion error"; // test lowercase
	if (caesar(caesar("DECRYPTED TEXT", 19), -19) != "DECRYPTED TEXT") throw "Assertion error"; // test negative keys for decryption
};

const testShiftLetter = () => {
	if (shiftLetter("L", 3, 65, 91) != "O") throw "Assertion error"; // test basic case
	if (shiftLetter("s", 14, 65, 122) throw "Assertion error"; // test wrap around, and custom ranges
};

These functions should go wherever their logic counterparts do! To make it easier to run batch tests, it might be helpful to package the unit tests into logical groups, or even to use a dedicated unit testing framework that handles the boilerplate for you.

But what happens when we’re trying to test something that’s not so easily encapsulated in a function? It’s not as straightforward to test UI elements and entire workflows with simple unit tests. What can we do next? Check out part 2 in this series: simulating the entire browser.

About the author
Jaden Baptista

Technical Writer

Recommended Articles

Powered byAlgolia Algolia Recommend

How to build a simulator for any feature — Part 2: Browser emulation
engineering

Jaden Baptista

Technical Writer

Building a Store Locator in React using Algolia, Mapbox, and Twilio – Part 3
engineering

Clément Sauvage

Software Engineer, Freelance

How to use Algolia as a game engine debugging tool in Rust
engineering

Gyula László

Senior Developer @ Starschema