$npx -y skills add LambdaTest/agent-skills --skill reqnroll-skillGenerates production-grade Reqnroll BDD automation scripts for web (Selenium 3/4) and mobile (Appium 2) testing in C#. Supports parallel NUnit execution locally and on TestMu AI cloud. Use when the user asks to write BDD tests, automate with Reqnroll, create .feature files, write
| 1 | ## Overview |
| 2 | |
| 3 | This skill guides QA engineers and test architects in writing production-grade Reqnroll |
| 4 | BDD tests for web and mobile automation in C#. It covers three execution paths — Selenium 4 |
| 5 | with manual driver management, Selenium 3 via the `Reqnroll.Actions` plugin, and Appium 2 |
| 6 | for Android mobile — all targeting TestMu AI (LambdaTest) cloud infrastructure. |
| 7 | |
| 8 | Reqnroll is the actively maintained open-source successor to SpecFlow. Existing SpecFlow |
| 9 | projects can migrate by swapping the NuGet package and namespace — no step definition |
| 10 | rewrites required. |
| 11 | |
| 12 | ## Key Execution Pathways |
| 13 | |
| 14 | **Framework Selection:** Distinguishes between Selenium 4 (manual `DriverFactory`), |
| 15 | Selenium 3 (`Reqnroll.SpecFlowCompatibility.Actions.LambdaTest` plugin with |
| 16 | `IBrowserInteractions`), and Appium 2 (`Appium.WebDriver`, `AndroidDriver`). |
| 17 | |
| 18 | **Cloud vs Local:** Reads `LT_USERNAME` and `LT_ACCESS_KEY` environment variables; |
| 19 | routes to `hub.lambdatest.com` (web) or `mobile-hub.lambdatest.com` (mobile). |
| 20 | Reports pass/fail to LambdaTest via `lambda-status` JavaScript executor calls in |
| 21 | `[AfterScenario]`. |
| 22 | |
| 23 | **Parallelism:** Uses `[assembly: Parallelizable(ParallelScope.Fixtures)]` with |
| 24 | `[assembly: LevelOfParallelism(N)]` (NUnit). State is shared between step definition |
| 25 | classes via `ScenarioContext` (injected by Reqnroll's DI container), not static fields. |
| 26 | |
| 27 | ## Core Technical Patterns |
| 28 | |
| 29 | ### Feature Files (Gherkin) |
| 30 | Each `.feature` file maps to one test class. Scenarios are tagged (`@tagName`) for |
| 31 | selective filtering with `dotnet test --filter "Category=tagName"`. Background steps |
| 32 | run before every scenario in the file; Scenario Outlines drive data-driven testing via |
| 33 | `Examples` tables. |
| 34 | |
| 35 | ### Step Definitions |
| 36 | Classes are decorated with `[Binding]`. Constructor injection (via Reqnroll's built-in |
| 37 | DI) receives `ScenarioContext` or shared context objects. One `[Binding]` class per |
| 38 | concern keeps files small. Regex-based step patterns use `(.*)` or typed captures |
| 39 | (`(\d+)`) — no attribute-level type converters needed for primitives. |
| 40 | |
| 41 | ### Hooks |
| 42 | `[BeforeScenario]` initialises the driver (stored in `ScenarioContext["driver"]`) and |
| 43 | navigates to the base URL. `[AfterScenario]` reads `_scenarioContext.TestError` (web) or |
| 44 | `TestContext.CurrentContext.Result.Outcome.Status` (mobile) to emit |
| 45 | `lambda-status=passed/failed` before `driver.Quit()`. |
| 46 | |
| 47 | ### ScenarioContext Driver Sharing |
| 48 | Drivers are stored as `_scenarioContext["driver"] = driver` and retrieved with |
| 49 | `scenarioContext["driver"] as IWebDriver`. This is required for parallel execution — |
| 50 | `static` driver fields cause race conditions. |
| 51 | |
| 52 | ### Explicit Waits |
| 53 | `WebDriverWait` with `Until(d => d.FindElement(locator))` replaces `ImplicitWait` |
| 54 | for dynamic content. A `WaitAndFind(By)` helper method encapsulates the 10-second |
| 55 | default; a `WaitAndClick(By, int timeout)` variant handles clickability. |
| 56 | |
| 57 | ## Cloud Integration (TestMu / LambdaTest) |
| 58 | |
| 59 | ### Web (Selenium 4) |
| 60 | ```csharp |
| 61 | var ltOptions = new Dictionary<string, object> |
| 62 | { |
| 63 | { "build", "Build Name" }, |
| 64 | { "project", "Project Name" }, |
| 65 | { "w3c", true }, |
| 66 | { "selenium_version", "4.38.0" }, |
| 67 | { "sessionName", scenarioName }, |
| 68 | { "platformName", "Windows 11" } |
| 69 | }; |
| 70 | var options = new ChromeOptions(); |
| 71 | options.BrowserVersion = "latest"; |
| 72 | options.AddAdditionalOption("LT:Options", ltOptions); |
| 73 | var driver = new RemoteWebDriver( |
| 74 | new Uri($"https://{userName}:{accessKey}@hub.lambdatest.com/wd/hub"), options); |
| 75 | ``` |
| 76 | |
| 77 | ### Mobile (Appium 2) |
| 78 | ```csharp |
| 79 | var ltOptions = new Dictionary<string, object> |
| 80 | { |
| 81 | { "build", "Build Name" }, |
| 82 | { "project", "Project Name" }, |
| 83 | { "w3c", true }, |
| 84 | { "app", "proverbial-android" }, // lt:// URI or pre-uploaded alias |
| 85 | { "platformName", "android" }, |
| 86 | { "deviceName", "Galaxy.*" }, |
| 87 | { "platformVersion", "14" }, |
| 88 | { "isRealMobile", true }, |
| 89 | { "autoAcceptAlerts", true }, |
| 90 | { "autoGrantPermissions", true }, |
| 91 | { "sessionName", scenarioName } |
| 92 | }; |
| 93 | var appiumOptions = new AppiumOptions(); |
| 94 | appiumOptions.AddAdditionalAppiumOption("LT:Options", ltOptions); |
| 95 | var driver = new AndroidDriver( |
| 96 | new Uri($"https://{userName}:{accessKey}@mobile-hub.lambdatest.com/wd/hub"), |
| 97 | appiumOptions); |
| 98 | ``` |
| 99 | |
| 100 | ### Reporting Pass/Fail |
| 101 | ```csharp |
| 102 | // Web (AfterScenario) |
| 103 | if (_scenarioContext.TestError == null) |
| 104 | ((IJavaScriptExecutor)driver).Exe |