ScreenshotNeo

BlogHow-to

How to Simplify Test Runs with Make

Create a clear `make test` command, model setup and build dependencies accurately, and keep parallel test runs safe.

By the ScreenshotNeo team4 October 20266 min read

A Makefile can give a project one memorable test command while keeping setup and build steps explicit. Add a phony test target that calls the repository’s existing test command, then add only the real prerequisites that command needs. For example:

.PHONY: test

test:
	./scripts/run-tests

Replace ./scripts/run-tests with the command your project already uses. From the directory containing the Makefile, run make test. Declaring test phony makes it an action rather than a generated file, so a file named test cannot accidentally suppress the recipe. See the GNU Make manual’s guidance on phony targets and rules.

1. Find the test command and prerequisites

Before changing the Makefile, identify how the project currently runs tests and what must happen first. A test command might depend on a compiled executable, generated fixtures, installed dependencies, or a local service. Keep the existing test runner responsible for its test behavior; use Make to provide the repeatable entry point and describe genuine prerequisite relationships.

  1. Find the test command developers already use, including required arguments.
  2. List preparation steps that must complete before that command can run.
  3. Identify whether test groups share mutable resources such as a database, port, or temporary directory.
  4. Check which make implementation the project supports before using implementation-specific features.

2. Add a phony test target

For a suite that needs no separate Make-managed preparation, use the minimal pattern above. A more explicit rule can include a real prerequisite, such as a program that the test script runs:

.PHONY: test

test: build/test-runner
	./scripts/run-tests

This prerequisite is illustrative: use the actual output and build rule in your repository. Make evaluates prerequisites before running the target recipe. If the test runner is a generated file, ensure its own rule describes how to build it. Avoid listing a phony action such as prepare as a prerequisite of a real output file: that can force the real file’s recipe to run every time Make considers it.

Make rules connect targets, prerequisites, and recipes. You can request a particular goal on the command line, so developers can run make test without changing the default goal or invoking unrelated work. The first target in the first makefile is generally the default goal; keep that in mind when arranging targets.

3. Make test scope explicit

If the repository has useful, distinct suites, give them separate names and let developers choose the scope they need:

.PHONY: test test-unit test-integration

test: test-unit test-integration

test-unit:
	./scripts/run-tests unit

test-integration:
	./scripts/run-tests integration

Adapt the commands to the project’s actual test runner. Keep an aggregate test target when it is useful as the standard full-suite command; developers can still request make test-unit or make test-integration directly. Consider the setup each suite needs, its runtime, and whether it shares resources before combining or parallelizing them.

4. Run independent tests in parallel carefully

GNU Make can run eligible prerequisites concurrently when invoked with a job count, for example make -j4 test. This is safe only when the dependency graph accurately represents which work depends on which other work. If two suites use the same database, port, temporary directory, or other mutable resource, parallel execution may cause conflicts. Add the needed ordering or run the affected work serially.

GNU Make documents .NOTPARALLEL as a way to serialize prerequisites of a selected target, or the invocation when used without prerequisites. GNU Make 4.4.1 also documents .WAIT for ordering prerequisites. Check the project’s Make dialect and version before relying on these controls; they are GNU Make features and should not be assumed to behave the same in other implementations. The governing principle is to represent real dependencies and shared-resource constraints accurately.

5. Troubleshooting

Symptom Likely cause Fix
make test says the target is up to date and skips the command A file or directory named test exists, and the target is not declared phony. Add .PHONY: test.
The test command runs before a required build step The build output is not listed as a prerequisite of test. Add the real required output as a prerequisite and ensure it has a valid build rule.
Tests interfere with each other under -j Parallel tasks share a resource, or their dependency relationship is missing from the Makefile. Model the dependency or shared-resource constraint; serialize the affected work if it cannot safely run concurrently.
Make rejects .WAIT or another control The installed implementation or version does not support the GNU Make feature used. Verify the supported Make dialect and use compatible rules or run the affected tasks serially.
The wrong tasks run when invoking plain make The first target in the first makefile is generally the default goal. Invoke the intended goal explicitly, such as make test, or arrange the default goal intentionally.
The test recipe runs but the test command fails The underlying test runner, environment, or its own prerequisites failed. Run the recipe’s command directly to inspect its output, then fix the runner or document and provide the needed environment setup.

6. Keep the target reliable and maintainable

  • Keep the entry point stable: developers and automation should be able to rely on the documented test goal.
  • Declare actions phony: include action targets such as test and suite aliases in .PHONY.
  • Model only genuine prerequisites: Make should know what must be built or prepared first, while real output targets retain useful incremental behavior.
  • Make scope visible: expose suite-specific goals when they give developers a useful way to select work.
  • Be deliberate with parallelism: independent suites can save elapsed time, but shared services and files need ordering or isolation.
  • Document environment requirements: note required variables, optional suites, and services near the project’s testing instructions.

Make itself does not determine the cost of running a test suite. Runtime depends on the commands and tests it invokes. Parallel jobs may shorten elapsed time for independent work while using more simultaneous resources; unsafe concurrency can also make results unreliable. Use the project’s actual test runner and resource limits when choosing a job count.

7. Or skip the browser setup

If a test workflow needs website screenshots, you can capture one with ScreenshotNeo, a website screenshot API and MCP server for developers. Its API documentation describes the request options. This cURL example returns a WebP capture:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The equivalent Python request is:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

And in Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month, no card required.

8. FAQ

Should the test target be named test?

That is a clear conventional name for the full suite. Choose names that match the repository’s existing task vocabulary and document them.

Can developers run only unit tests?

Yes. Give the unit suite its own goal, such as test-unit, and invoke that goal directly.

Does Make replace a test framework?

No. The target is a convenient interface to the project’s test command; the test runner still performs the tests.

Does every repository support the same Make features?

No. Confirm the implementation and version contributors use before adopting GNU-specific controls.

Sources