C# NUnit中实现方法仅执行一次的方案排查——继承场景下[OneTimeSetUp]与[SetUpFixture]失效问题
Hey there! I get where you're coming from—switching from Java's Selenium annotations to NUnit can have a few gotchas, especially with suite-level setup. Let's figure out why your current approach isn't working and fix that duplicate folder creation issue.
Why Your Current Code Isn't Behaving as Expected
The problem with using [SetUpFixture] on your base Controls class is that NUnit treats each test fixture (your UnitTest1 and UnitTest2 classes) as separate suites when they inherit from a [SetUpFixture]-marked base. That means every time a subclass is initialized as a test fixture, it triggers the base class's [OneTimeSetUp]—hence your duplicate folder creation.
[SetUpFixture] is designed to be a standalone class for namespace-level or global suite setup, not a base class for inheritance. Let's adjust your structure to match NUnit's intended behavior.
The Correct Approach
We'll split your code into two distinct parts:
- A global suite-level setup class to handle the one-time folder creation.
- A regular base class for test-specific setup (like initializing drivers) that your test classes can inherit from.
Step 1: Create a Global Suite Setup Class
Make a standalone class marked with [SetUpFixture] in your root namespace. This will run once before any tests execute, regardless of how many test fixtures you have.
namespace Core { [SetUpFixture] public class GlobalTestSuiteSetup { [OneTimeSetUp] public void BeforeSuite() { // Your logic to create the unique report folder goes here // This will execute exactly once when the test suite starts Console.WriteLine("Creating unique report folder - only runs once!"); } [OneTimeTearDown] public void AfterSuite() { // Optional: Cleanup logic that runs once after all tests finish } } }
Step 2: Adjust Your Controls Base Class
Turn Controls into a regular base class (no [SetUpFixture] or [TestFixture] attributes) that handles per-test or per-fixture setup/teardown for your drivers.
namespace Core.Framework.Controls { // No [SetUpFixture] or [TestFixture] here—this is a plain base class public class Controls { protected IWebDriver _driver; protected string BrowserName { get; set; } // Per-test setup (runs before each test method) public void SetUpDriver(string browser) { BrowserName = browser; // Your driver initialization logic here (e.g., ChromeDriver, FirefoxDriver) // Example: _driver = new ChromeDriver(); } // Per-test teardown (runs after each test method) [TearDown] public void TearDownDriver() { _driver?.Quit(); } // Your browser test case source public static IEnumerable<string> BrowsersToRunWith() { yield return "Chrome"; yield return "Firefox"; } } }
Step 3: Update Your Test Classes
Your test classes remain mostly the same—they inherit from Controls and run their tests as before. The global setup will handle the one-time folder creation automatically.
namespace Core { [TestFixture] [Parallelizable] public class UnitTest1 : Controls { [Test] [TestCaseSource(typeof(Controls), "BrowsersToRunWith")] public void Bing(string browser) { SetUpDriver(browser); Class1 class1 = new Class1(_driver); class1.LaunchURL("http://bing.com"); class1.EnterTextInBing("Parallel Execution using C#"); Thread.Sleep(3000); } } }
namespace Core { [TestFixture] [Parallelizable] public class UnitTest2 : Controls { [Test] [TestCaseSource(typeof(Controls), "BrowsersToRunWith")] public void Google(string browser) { SetUpDriver(browser); Class1 class1 = new Class1(_driver); class1.LaunchURL("http://google.com"); } } }
Key Notes to Ensure This Works
- Namespace Placement: The
[SetUpFixture]class should be in the root namespace (likeCore) to apply to all test classes in that namespace and its sub-namespaces. If you put it in a sub-namespace, it will only apply to tests within that sub-namespace. - No Inheritance for SetUpFixture: Never inherit from a
[SetUpFixture]class—this is what caused your original duplicate execution issue. Keep it standalone. - Parallel Safety: The global
[OneTimeSetUp]runs before any parallel tests start, so your folder creation logic is safe from race conditions.
内容的提问来源于stack exchange,提问作者Naseem

