skip to content

In Selenium's ChromeOptions and FirefoxOptions, how do browser preferences differ from launch arguments?

level: middleimportance: should knowfreq 57%

answer

  1. Two doors: command line versus profile
  2. One is a switch, one a setting
  3. Chrome has no typed preference setter
  4. Firefox takes them one key at a time
  5. Neither route validates the key

basics

~20 s

A launch argument is a command-line switch handed to the browser process at startup; a preference is a setting written into the profile that session runs with. Chrome takes preferences as a map through setExperimentalOption; Firefox has addPreference.

solid answer

~40 s

Arguments and preferences enter the browser through different doors. `addArguments("--incognito")` appends a switch to the browser process's command line; a preference is something the browser would normally keep in a profile, the kind of setting you would change on a settings page or in `about:config`. The APIs are asymmetric: `ChromeOptions` and `EdgeOptions` have no typed preference setter, so you build a `Map<String, Object>` and call `setExperimentalOption("prefs", map)` with dotted keys such as `intl.accept_languages`, while `FirefoxOptions.addPreference(key, value)` takes them one at a time using `about:config` names such as `dom.webnotifications.enabled`. Neither route is validated — a wrong key or a wrong value type is silently ignored — and because both drivers create a fresh profile per session by default, the preferences apply to that session only and are gone when it ends.

code

java · 24 lines
java
import java.util.HashMap;
import java.util.Map;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class RecipeTimerNotificationTest {
  public static void main(String[] args) {
    Map<String, Object> prefs = new HashMap<>();
    prefs.put("profile.default_content_setting_values.notifications", 2);
    prefs.put("intl.accept_languages", "en-GB,en");

    ChromeOptions options = new ChromeOptions();
    options.setExperimentalOption("prefs", prefs);

    WebDriver driver = new ChromeDriver(options);
    try {
      driver.get("https://recipes.example.com/recipes/lemon-risotto");
      System.out.println(driver.getTitle());
    } finally {
      driver.quit();
    }
  }
}

go deeper

for a junior

Be able to state the difference in one line: a switch goes on the browser's command line, a preference goes into the profile the session uses. Naming the Chrome and Firefox entry points is a strong answer at this level.

for a middle

Explain the API asymmetry and why it exists: Chrome takes an untyped map because Selenium models none of its dotted keys, while Firefox exposes a typed per-key setter. Mention that neither side validates anything.

for a senior

Show how you debug a preference that appears to do nothing, and be ready to say when suppressing a browser prompt is legitimate setup and when it is quietly disabling the behaviour the test was meant to exercise.

for a principal

Own the policy on how much browser tuning a suite is allowed to carry. Every suppressed prompt and forced locale moves the tested browser further from a user's, and somebody has to decide how far is acceptable.

## Two different doors into the browser An options object can reach the browser two ways, and they are not interchangeable. - A **launch argument** is a command-line switch given to the browser process when it starts: `--incognito`, `--headless=new`, `--lang=en-GB`. It goes in through `addArguments`, it is a plain string, and it exists only for the lifetime of that process. - A **preference** is a setting the browser normally keeps inside a user profile — the thing you would otherwise change on a settings page or in `about:config`. It goes in through a different API per browser, and the driver writes it into the profile the session runs with. The practical rule: if the browser documents it as a command-line switch it is an argument; if you would find it in the browser's own settings, it is a preference. ## The API on each side | Concern | Chrome and Edge | Firefox | |---|---|---| | Launch switch | `addArguments("--headless=new")` | `addArguments("-headless")` | | Preference | `setExperimentalOption("prefs", map)` | `addPreference(key, value)` | | Key style | dotted paths, e.g. `intl.accept_languages` | `about:config` names, e.g. `dom.webnotifications.enabled` | | Typed? | no — an untyped `Map<String, Object>` | yes — overloads for `String`, `int` and `boolean` | | Lifetime | the session's profile, discarded with it | the session's profile, discarded with it | The asymmetry is the part people get wrong. `ChromeOptions` and `EdgeOptions` have **no** typed preference setter, because Chrome's preference namespace is enormous and Selenium models none of it; the whole map is handed over under the `prefs` name through `setExperimentalOption`. `FirefoxOptions` does model it, one key at a time. ## A worked case: the recipe manager's notification prompt A **recipe collection manager** asks for notification permission the first time a cooking timer is started, and the native permission bubble sits over the "Save to collection" button. That is a preference, not a switch: ```java Map<String, Object> prefs = new HashMap<>(); prefs.put("profile.default_content_setting_values.notifications", 2); prefs.put("intl.accept_languages", "en-GB,en"); ChromeOptions chrome = new ChromeOptions(); chrome.setExperimentalOption("prefs", prefs); FirefoxOptions firefox = new FirefoxOptions(); firefox.addPreference("dom.webnotifications.enabled", false); firefox.addPreference("intl.accept_languages", "en-GB,en"); ``` The Chrome value `2` is the browser's own encoding for "block" on a content setting; Firefox turns the whole Notifications API off with a boolean. Same intent, two vocabularies, because both vocabularies belong to the browsers rather than to Selenium. ## Deciding which one a setting needs 1. Look for the setting in the browser's own settings UI or `about:config`. If it is there, it is a preference. 2. Otherwise check whether the browser documents it as a command-line switch. If so, it is an argument. 3. If it appears in both forms, prefer the preference: it says what you want rather than how the process was invoked, and it survives changes to how the browser is launched. ## Where it goes wrong - **Passing a preference as an argument.** A dotted key such as `profile.default_content_setting_values.notifications` is not a switch; handed to `addArguments` it is ignored and the prompt still appears. - **Expecting validation.** Neither route checks anything. A misspelled preference key, or a string where the browser wants an integer, is dropped silently, so a wrong key and a wrong value look identical from the test's side. - **Expecting persistence.** Both drivers create a fresh profile per session by default, so the preferences you set apply to that session and vanish with it. Nothing you set leaks into the next run, and nothing from the last run leaks into this one. - **Assuming Firefox keys work in Chrome.** `dom.webnotifications.enabled` means nothing to Chrome and `profile.default_content_setting_values.notifications` means nothing to Firefox; the preference namespaces are entirely separate. - **Reaching for a preference when a page-level fix exists.** Blocking a prompt at the browser level is right when the prompt is incidental to the test; when the prompt is the behaviour under test, suppressing it hides the thing you meant to check. ## What this buys a suite Set together, arguments and preferences let a run start a browser that is boring and predictable: one language, no permission prompts, no state carried in from a previous session. That determinism is the real point of the options object — the test does not have to click its way past browser chrome before it can touch the recipe app at all.

  • Why are Chrome preferences set through setExperimentalOption rather than a typed method?
    Because the preference namespace belongs to Chrome, is enormous, and changes with the browser. Selenium models none of those dotted keys, so it accepts a `Map<String, Object>` under the `prefs` name and passes it through untouched. The cost is no compile-time checking: a wrong key or a wrong value type is simply ignored.
  • A preference you set does not seem to take effect. How do you tell whether the key or the mechanism is wrong?
    Prove the mechanism first with a preference whose effect is obvious, then check the key by opening the browser's own settings page inside the running session and looking at the value. A silently ignored key is usually a typo, a value of the wrong type, or a setting the browser only reads when the profile is created.
  • Do the preferences you set survive into the next session?
    Not by default. Both drivers create a fresh profile per session, so preferences apply to that session and disappear with it. They persist only when the session is pointed at a directory you keep on disk, which brings its own problems: state the test did not create, and a directory two sessions cannot share.

An argument is what you shout at the browser on the doorstep as it starts; a preference is a line written into the settings notebook it opens once it is inside.

saying these in an interview costs you the question

  • Passes a dotted preference key to addArguments as a switch
  • Expects Selenium or the browser to reject a misspelled preference key
  • Assumes ChromeOptions has a typed addPreference method
  • Thinks preferences set for one session persist into the next
  • Uses Firefox about:config names in Chrome's preferences map