C#窗体按钮功能单元测试示例请求:基于NUnit/XUnit实现Save与Delete按钮测试
Hey there, I get where you're coming from—testing WinForms button click events can feel tricky because most tutorials focus on pure business logic, not UI-coupled code. The key here is to decouple your business logic from the UI first, so you can test the core functionality with NUnit/xUnit without needing to run the entire form. Let me walk you through how to do this with examples:
1. Refactor Your Code to Separate Logic from UI
Right now, your database operations and validation logic are tightly tied to UI controls (like PrNameTb or CatCb), which makes them hard to test. Let's move that logic into a separate service class:
Step 1.1: Create a Product Service Class
This class will handle all database interactions and validation, using plain parameters instead of UI controls:
using System.Data.SqlClient; public class ProductService { private readonly string _connectionString; // Inject the connection string for flexibility (test vs production) public ProductService(string connectionString) { _connectionString = connectionString; } public string AddProduct(string productName, string category, string quantity, string price) { // Input validation (same as your original code) if (string.IsNullOrEmpty(productName) || string.IsNullOrEmpty(category) || string.IsNullOrEmpty(quantity) || string.IsNullOrEmpty(price)) { return "Missing Information"; } try { // Use 'using' to automatically close connections using (var con = new SqlConnection(_connectionString)) { con.Open(); var cmd = new SqlCommand( "INSERT INTO ProductTbl (PrName, PrCat, PrQty, PrPrice) VALUES (@PN, @PC, @PQ, @PP)", con); cmd.Parameters.AddWithValue("@PN", productName); cmd.Parameters.AddWithValue("@PC", category); cmd.Parameters.AddWithValue("@PQ", quantity); cmd.Parameters.AddWithValue("@PP", price); cmd.ExecuteNonQuery(); return "Product Added"; } } catch (Exception ex) { return ex.Message; } } public string DeleteProduct(int productId) { if (productId == 0) { return "Select an Product"; } try { using (var con = new SqlConnection(_connectionString)) { con.Open(); var cmd = new SqlCommand( "DELETE FROM ProductTbl WHERE PrId = @PKey", con); cmd.Parameters.AddWithValue("@PKey", productId); cmd.ExecuteNonQuery(); return "Product Deleted"; } } catch (Exception ex) { return ex.Message; } } }
Step 1.2: Simplify Your Button Click Events
Now update your form's code to use the service instead of embedding logic directly:
public partial class YourProductForm : Form { private readonly ProductService _productService; public int Key { get; set; } // Keep your existing Key property // Initialize the service in the form constructor public YourProductForm(string connectionString) { InitializeComponent(); _productService = new ProductService(connectionString); } private void Savebtn_Click(object sender, EventArgs e) { string category = CatCb.SelectedItem?.ToString(); string result = _productService.AddProduct(PrNameTb.Text, category, QtyTb.Text, PriceTb.Text); MessageBox.Show(result); if (result == "Product Added") { DisplayProducts(); Clear(); } } private void DeleteBtn_Click(object sender, EventArgs e) { string result = _productService.DeleteProduct(Key); MessageBox.Show(result); if (result == "Product Deleted") { DisplayProducts(); Clear(); } } // Keep your existing DisplayProducts() and Clear() methods private void DisplayProducts() { /* ... */ } private void Clear() { /* ... */ } }
2. Write Unit Tests for the Business Logic
Now you can easily test the ProductService with xUnit or NUnit. For database tests, use a dedicated test database (not your production one) to avoid side effects.
Example xUnit Tests
using Xunit; using System.Data.SqlClient; public class ProductServiceTests { // Use a test database connection string (update this to match your setup) private readonly string _testConnectionString = "Server=localhost;Database=ProductTestDB;Trusted_Connection=True;"; [Fact] public void AddProduct_WithMissingRequiredFields_ReturnsMissingInfoMessage() { // Arrange var service = new ProductService(_testConnectionString); // Act: Pass an empty product name var result = service.AddProduct("", "Electronics", "10", "50"); // Assert Assert.Equal("Missing Information", result); } [Fact] public void AddProduct_WithValidData_ReturnsSuccessMessage() { // Arrange var service = new ProductService(_testConnectionString); string testProductName = "Test Wireless Mouse"; // Act var result = service.AddProduct(testProductName, "Electronics", "20", "25"); // Assert Assert.Equal("Product Added", result); // Optional: Verify the product was actually added to the database using (var con = new SqlConnection(_testConnectionString)) { con.Open(); var cmd = new SqlCommand("SELECT COUNT(*) FROM ProductTbl WHERE PrName = @PN", con); cmd.Parameters.AddWithValue("@PN", testProductName); int count = (int)cmd.ExecuteScalar(); Assert.Equal(1, count); } } [Fact] public void DeleteProduct_WithZeroId_ReturnsSelectProductMessage() { // Arrange var service = new ProductService(_testConnectionString); // Act var result = service.DeleteProduct(0); // Assert Assert.Equal("Select an Product", result); } [Fact] public void DeleteProduct_WithValidId_ReturnsSuccessMessage() { // Arrange var service = new ProductService(_testConnectionString); // First add a test product to delete service.AddProduct("Test Keyboard", "Electronics", "15", "40"); // Get the ID of the new product int productId = 0; using (var con = new SqlConnection(_testConnectionString)) { con.Open(); var cmd = new SqlCommand("SELECT PrId FROM ProductTbl WHERE PrName = @PN", con); cmd.Parameters.AddWithValue("@PN", "Test Keyboard"); productId = (int)cmd.ExecuteScalar(); } // Act var result = service.DeleteProduct(productId); // Assert Assert.Equal("Product Deleted", result); // Verify the product was removed using (var con = new SqlConnection(_testConnectionString)) { con.Open(); var cmd = new SqlCommand("SELECT COUNT(*) FROM ProductTbl WHERE PrId = @PKey", con); cmd.Parameters.AddWithValue("@PKey", productId); int count = (int)cmd.ExecuteScalar(); Assert.Equal(0, count); } } }
3. (Optional) UI Automation Testing for Button Clicks
If you want to test the actual button click behavior (like verifying MessageBox pops up or UI updates), use a tool like WinAppDriver for UI automation. Here's a quick example:
using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Windows; using Xunit; public class ProductFormUITests { private WindowsDriver<WindowsElement> _driver; [Fact] public void SaveButton_Click_WithEmptyFields_ShowsMissingInfoMessageBox() { // Initialize WinAppDriver (make sure it's running in the background) var appOptions = new AppiumOptions(); appOptions.AddAdditionalCapability("app", @"C:\Path\To\Your\WinFormsApp.exe"); _driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), appOptions); // Find and click the save button var saveButton = _driver.FindElementByAccessibilityId("Savebtn"); saveButton.Click(); // Verify the message box appears with the correct text var messageBox = _driver.FindElementByClassName("#32770"); var messageText = messageBox.FindElementByClassName("Static").Text; Assert.Equal("Missing Information", messageText); // Close the message box var okButton = messageBox.FindElementByName("OK"); okButton.Click(); // Clean up _driver.Quit(); } }
内容的提问来源于stack exchange,提问作者Dora99

