Files
sharp-basic/test/Common.Test/ResultTests.cs
T
2025-12-30 09:17:25 +08:00

868 lines
22 KiB
C#

using Common.Results;
using Common.ExpandException;
namespace Common.Test;
/// <summary>
/// Result 模式测试
/// 展示函数式错误处理的所有功能
/// </summary>
[TestClass]
public class ResultTests
{
#region 基础创建和状态测试
/// <summary>
/// 测试:创建成功的 Result
/// </summary>
[TestMethod]
public void Success_CreatesSuccessResult()
{
// Act: 创建成功的无返回值 Result
var result = Result.Success();
// Assert
Assert.IsTrue(result.IsSuccess);
Assert.IsFalse(result.IsFailure);
Assert.IsNull(result.Exception);
Assert.IsNull(result.ErrorMessage);
Assert.IsNull(result.ErrorCode);
}
/// <summary>
/// 测试:创建成功的 Result<T>
/// </summary>
[TestMethod]
public void Success_WithValue_CreatesSuccessResultWithValue()
{
// Act: 创建带值的成功 Result
var result = Result.Success(42);
// Assert
Assert.IsTrue(result.IsSuccess);
Assert.AreEqual(42, result.Value);
}
/// <summary>
/// 测试:创建失败的 Result(使用错误消息)
/// </summary>
[TestMethod]
public void Failure_WithMessage_CreatesFailureResult()
{
// Act
var result = Result.Failure<int>("Something went wrong");
// Assert
Assert.IsFalse(result.IsSuccess);
Assert.IsTrue(result.IsFailure);
Assert.AreEqual("Something went wrong", result.ErrorMessage);
Assert.IsNull(result.ErrorCode);
}
/// <summary>
/// 测试:创建失败的 Result(使用异常)
/// </summary>
[TestMethod]
public void Failure_WithException_CreatesFailureResult()
{
// Arrange
var exception = new InvalidOperationException("Invalid operation");
// Act
var result = Result.Failure<int>(exception);
// Assert
Assert.IsTrue(result.IsFailure);
Assert.AreSame(exception, result.Exception);
Assert.AreEqual("Invalid operation", result.ErrorMessage);
}
/// <summary>
/// 测试:创建带错误码的失败 Result
/// </summary>
[TestMethod]
public void Failure_WithErrorCode_CreatesFailureResultWithCode()
{
// Act
var result = Result.Failure<int>(404L, "Not found");
// Assert
Assert.IsTrue(result.IsFailure);
Assert.AreEqual(404L, result.ErrorCode);
Assert.AreEqual("Not found", result.ErrorMessage);
}
/// <summary>
/// 测试:从 ExceptionExpand 创建失败 Result
/// </summary>
[TestMethod]
public void Failure_WithExceptionExpand_ExtractsErrorCode()
{
// Arrange
var exception = new ExceptionExpand(1001L, "Custom error", "Try again");
// Act
var result = Result.Failure<int>(exception);
// Assert
Assert.IsTrue(result.IsFailure);
Assert.AreEqual(1001L, result.ErrorCode);
Assert.AreEqual("Custom error", result.ErrorMessage);
Assert.AreSame(exception, result.Exception);
}
#endregion
#region Try 模式测试
/// <summary>
/// 测试:Try 捕获成功的操作
/// </summary>
[TestMethod]
public void Try_SuccessfulAction_ReturnsSuccess()
{
// Arrange
var executed = false;
// Act
var result = Result.Try(() => { executed = true; });
// Assert
Assert.IsTrue(result.IsSuccess);
Assert.IsTrue(executed);
}
/// <summary>
/// 测试:Try 捕获失败的操作
/// </summary>
[TestMethod]
public void Try_FailingAction_ReturnsFailure()
{
// Act
var result = Result.Try(() => { throw new InvalidOperationException("Operation failed"); });
// Assert
Assert.IsTrue(result.IsFailure);
Assert.IsInstanceOfType(result.Exception, typeof(InvalidOperationException));
Assert.AreEqual("Operation failed", result.ErrorMessage);
}
/// <summary>
/// 测试:Try 捕获返回值的成功操作
/// </summary>
[TestMethod]
public void Try_SuccessfulFunc_ReturnsSuccessWithValue()
{
// Act
var result = Result.Try(() => int.Parse("123"));
// Assert
Assert.IsTrue(result.IsSuccess);
Assert.AreEqual(123, result.Value);
}
/// <summary>
/// 测试:Try 捕获返回值的失败操作
/// </summary>
[TestMethod]
public void Try_FailingFunc_ReturnsFailure()
{
// Act
var result = Result.Try(() => int.Parse("invalid"));
// Assert
Assert.IsTrue(result.IsFailure);
Assert.IsInstanceOfType(result.Exception, typeof(FormatException));
}
/// <summary>
/// 测试:TryAsync 异步操作
/// </summary>
[TestMethod]
public async Task TryAsync_SuccessfulTask_ReturnsSuccess()
{
// Act
var result = await Result.TryAsync(async () => { await Task.Delay(10); });
// Assert
Assert.IsTrue(result.IsSuccess);
}
/// <summary>
/// 测试:TryAsync 异步操作返回值
/// </summary>
[TestMethod]
public async Task TryAsync_SuccessfulTaskWithValue_ReturnsSuccessWithValue()
{
// Act
var result = await Result.TryAsync(async () =>
{
await Task.Delay(10);
return 42;
});
// Assert
Assert.IsTrue(result.IsSuccess);
Assert.AreEqual(42, result.Value);
}
#endregion
#region 值提取测试
/// <summary>
/// 测试:GetOrNull 获取引用类型
/// </summary>
[TestMethod]
public void GetOrNull_SuccessResult_ReturnsValue()
{
// Arrange
var result = Result.Success("test");
// Act
var value = result.GetOrNull();
// Assert
Assert.AreEqual("test", value);
}
/// <summary>
/// 测试:GetOrNull 失败返回 null
/// </summary>
[TestMethod]
public void GetOrNull_FailureResult_ReturnsNull()
{
// Arrange
var result = Result.Failure<string>("error");
// Act
var value = result.GetOrNull();
// Assert
Assert.IsNull(value);
}
/// <summary>
/// 测试:GetOrNullable 获取值类型
/// </summary>
[TestMethod]
public void GetOrNullable_SuccessResult_ReturnsValue()
{
// Arrange
var result = Result.Success(42);
// Act
var value = result.GetOrNullable();
// Assert
Assert.AreEqual(42, value);
}
/// <summary>
/// 测试:GetOrDefault 获取默认值
/// </summary>
[TestMethod]
public void GetOrDefault_FailureResult_ReturnsDefault()
{
// Arrange
var result = Result.Failure<int>("error");
// Act
var value = result.GetOrDefault(999);
// Assert
Assert.AreEqual(999, value);
}
/// <summary>
/// 测试:GetOrThrow 成功时返回值
/// </summary>
[TestMethod]
public void GetOrThrow_SuccessResult_ReturnsValue()
{
// Arrange
var result = Result.Success(42);
// Act
var value = result.GetOrThrow();
// Assert
Assert.AreEqual(42, value);
}
/// <summary>
/// 测试:GetOrThrow 失败时抛出异常
/// </summary>
[TestMethod]
public void GetOrThrow_FailureResult_ThrowsException()
{
// Arrange
var exception = new InvalidOperationException("test error");
var result = Result.Failure<int>(exception);
// Act & Assert
var thrown = Assert.ThrowsException<InvalidOperationException>(() => { result.GetOrThrow(); });
Assert.AreSame(exception, thrown);
}
/// <summary>
/// 测试:GetOrElse 使用函数提供替代值
/// </summary>
[TestMethod]
public void GetOrElse_FailureResult_CallsAlternativeProvider()
{
// Arrange
var result = Result.Failure<int>("error");
// Act
var value = result.GetOrElse(() => 100);
// Assert
Assert.AreEqual(100, value);
}
#endregion
#region 函数式操作测试
/// <summary>
/// 测试:Map 转换成功的值
/// </summary>
[TestMethod]
public void Map_SuccessResult_TransformsValue()
{
// Arrange
var result = Result.Success(10);
// Act
var mapped = result.Map(x => x * 2);
// Assert
Assert.IsTrue(mapped.IsSuccess);
Assert.AreEqual(20, mapped.Value);
}
/// <summary>
/// 测试:Map 传播失败
/// </summary>
[TestMethod]
public void Map_FailureResult_PropagatesFailure()
{
// Arrange
var result = Result.Failure<int>("error");
// Act
var mapped = result.Map(x => x * 2);
// Assert
Assert.IsTrue(mapped.IsFailure);
Assert.AreEqual("error", mapped.ErrorMessage);
}
/// <summary>
/// 测试:Bind 链式操作
/// </summary>
[TestMethod]
public void Bind_SuccessResult_ChainsOperations()
{
// Arrange
var result = Result.Success(10);
// Act
var bound = result
.Bind(x => x > 5 ? Result.Success(x * 2) : Result.Failure<int>("Too small"))
.Bind(x => Result.Success(x.ToString()));
// Assert
Assert.IsTrue(bound.IsSuccess);
Assert.AreEqual("20", bound.Value);
}
/// <summary>
/// 测试:Bind 中断失败链
/// </summary>
[TestMethod]
public void Bind_FailureInChain_StopsExecution()
{
// Arrange
var result = Result.Success(3);
var secondBindExecuted = false;
// Act
var bound = result
.Bind(x => x > 5 ? Result.Success(x * 2) : Result.Failure<int>("Too small"))
.Bind(x =>
{
secondBindExecuted = true;
return Result.Success(x * 2);
});
// Assert
Assert.IsTrue(bound.IsFailure);
Assert.IsFalse(secondBindExecuted); // 第二个 Bind 不应该执行
Assert.AreEqual("Too small", bound.ErrorMessage);
}
/// <summary>
/// 测试:Where 过滤值
/// </summary>
[TestMethod]
public void Where_PredicateSatisfied_ReturnsSuccess()
{
// Arrange
var result = Result.Success(10);
// Act
var filtered = result.Where(x => x > 5, "Value must be greater than 5");
// Assert
Assert.IsTrue(filtered.IsSuccess);
Assert.AreEqual(10, filtered.Value);
}
/// <summary>
/// 测试:Where 过滤失败
/// </summary>
[TestMethod]
public void Where_PredicateNotSatisfied_ReturnsFailure()
{
// Arrange
var result = Result.Success(3);
// Act
var filtered = result.Where(x => x > 5, "Value must be greater than 5");
// Assert
Assert.IsTrue(filtered.IsFailure);
Assert.AreEqual("Value must be greater than 5", filtered.ErrorMessage);
}
/// <summary>
/// 测试:Ensure 验证
/// </summary>
[TestMethod]
public void Ensure_ValidationPasses_ReturnsSuccess()
{
// Arrange
var result = Result.Success("test@example.com");
// Act
var ensured = result
.Ensure(email => email.Contains("@"), "Invalid email format")
.Ensure(email => email.Length > 5, "Email too short");
// Assert
Assert.IsTrue(ensured.IsSuccess);
}
/// <summary>
/// 测试:Ensure 验证失败
/// </summary>
[TestMethod]
public void Ensure_ValidationFails_ReturnsFailure()
{
// Arrange
var result = Result.Success("invalid");
// Act
var ensured = result.Ensure(email => email.Contains("@"), "Invalid email format");
// Assert
Assert.IsTrue(ensured.IsFailure);
Assert.AreEqual("Invalid email format", ensured.ErrorMessage);
}
#endregion
#region 条件执行测试
/// <summary>
/// 测试:OnSuccess 成功时执行
/// </summary>
[TestMethod]
public void OnSuccess_SuccessResult_ExecutesAction()
{
// Arrange
var result = Result.Success(42);
var executed = false;
var capturedValue = 0;
// Act
result.OnSuccess(value =>
{
executed = true;
capturedValue = value;
});
// Assert
Assert.IsTrue(executed);
Assert.AreEqual(42, capturedValue);
}
/// <summary>
/// 测试:OnSuccess 失败时不执行
/// </summary>
[TestMethod]
public void OnSuccess_FailureResult_DoesNotExecute()
{
// Arrange
var result = Result.Failure<int>("error");
var executed = false;
// Act
result.OnSuccess(value => executed = true);
// Assert
Assert.IsFalse(executed);
}
/// <summary>
/// 测试:OnFailure 失败时执行
/// </summary>
[TestMethod]
public void OnFailure_FailureResult_ExecutesAction()
{
// Arrange
var result = Result.Failure<int>(404L, "Not found");
var executed = false;
string? capturedMessage = null;
long? capturedCode = null;
// Act
result.OnFailure((ex, msg, code) =>
{
executed = true;
capturedMessage = msg;
capturedCode = code;
});
// Assert
Assert.IsTrue(executed);
Assert.AreEqual("Not found", capturedMessage);
Assert.AreEqual(404L, capturedCode);
}
/// <summary>
/// 测试:Match 模式匹配
/// </summary>
[TestMethod]
public void Match_SuccessResult_CallsSuccessHandler()
{
// Arrange
var result = Result.Success(42);
// Act
var message = result.Match(
value => $"Success: {value}",
(ex, msg) => $"Error: {msg}"
);
// Assert
Assert.AreEqual("Success: 42", message);
}
/// <summary>
/// 测试:Match 失败结果
/// </summary>
[TestMethod]
public void Match_FailureResult_CallsFailureHandler()
{
// Arrange
var result = Result.Failure<int>("Something went wrong");
// Act
var message = result.Match(
value => $"Success: {value}",
(ex, msg) => $"Error: {msg}"
);
// Assert
Assert.AreEqual("Error: Something went wrong", message);
}
#endregion
#region LINQ 查询语法测试
/// <summary>
/// 测试:使用 LINQ 查询语法组合 Result
/// </summary>
[TestMethod]
public void LinqQuery_CombinesResults()
{
// Arrange
var result1 = Result.Success(5);
var result2 = Result.Success(10);
// Act: 使用 LINQ 查询语法
var combined = from x in result1
from y in result2
where x > 0 && y > 0
select x + y;
// Assert
Assert.IsTrue(combined.IsSuccess);
Assert.AreEqual(15, combined.Value);
}
/// <summary>
/// 测试:LINQ 查询中的失败传播
/// </summary>
[TestMethod]
public void LinqQuery_PropagatesFailure()
{
// Arrange
var result1 = Result.Success(5);
var result2 = Result.Failure<int>("Error in result2");
// Act
var combined = from x in result1
from y in result2
select x + y;
// Assert
Assert.IsTrue(combined.IsFailure);
Assert.AreEqual("Error in result2", combined.ErrorMessage);
}
#endregion
#region 错误码相关测试
/// <summary>
/// 测试:HasErrorCode 检查错误码
/// </summary>
[TestMethod]
public void HasErrorCode_MatchingCode_ReturnsTrue()
{
// Arrange
var result = Result.Failure<int>(404L, "Not found");
// Act & Assert
Assert.IsTrue(result.HasErrorCode(404L));
Assert.IsFalse(result.HasErrorCode(500L));
}
/// <summary>
/// 测试:GetErrorCodeOrDefault 获取错误码
/// </summary>
[TestMethod]
public void GetErrorCodeOrDefault_ReturnsCodeOrDefault()
{
// Arrange
var failureResult = Result.Failure<int>(404L, "Not found");
var successResult = Result.Success(42);
// Act & Assert
Assert.AreEqual(404L, failureResult.GetErrorCodeOrDefault());
Assert.AreEqual(-1L, successResult.GetErrorCodeOrDefault());
Assert.AreEqual(999L, successResult.GetErrorCodeOrDefault(999L));
}
/// <summary>
/// 测试:FromExceptionExpand 转换自定义异常
/// </summary>
[TestMethod]
public void FromExceptionExpand_CreatesResultWithErrorCode()
{
// Arrange
var exception = new ExceptionExpand(1001L, "Business error", "Check input");
// Act
var result = ResultExtensions.FromExceptionExpand<string>(exception);
// Assert
Assert.IsTrue(result.IsFailure);
Assert.AreEqual(1001L, result.ErrorCode);
Assert.AreEqual("Business error", result.ErrorMessage);
Assert.AreSame(exception, result.Exception);
}
#endregion
#region 异步操作测试
/// <summary>
/// 测试:MapAsync 异步转换
/// </summary>
[TestMethod]
public async Task MapAsync_TransformsValueAsynchronously()
{
// Arrange
var result = Task.FromResult(Result.Success(10));
// Act
var mapped = await result.MapAsync(async value =>
{
await Task.Delay(10);
return value * 2;
});
// Assert
Assert.IsTrue(mapped.IsSuccess);
Assert.AreEqual(20, mapped.Value);
}
/// <summary>
/// 测试:BindAsync 异步链式操作
/// </summary>
[TestMethod]
public async Task BindAsync_ChainsAsynchronously()
{
// Arrange
var result = Task.FromResult(Result.Success(10));
// Act
var bound = await result.BindAsync(async value =>
{
await Task.Delay(10);
return value > 5 ? Result.Success(value * 2) : Result.Failure<int>("Too small");
});
// Assert
Assert.IsTrue(bound.IsSuccess);
Assert.AreEqual(20, bound.Value);
}
/// <summary>
/// 测试:DoOnSuccessAsync 异步副作用
/// </summary>
[TestMethod]
public async Task DoOnSuccessAsync_ExecutesAsyncAction()
{
// Arrange
var result = Task.FromResult(Result.Success(42));
var executed = false;
// Act
await result.DoOnSuccessAsync(async value =>
{
await Task.Delay(10);
executed = true;
});
// Assert
Assert.IsTrue(executed);
}
#endregion
#region 实际应用场景测试
/// <summary>
/// 场景测试:验证用户输入
/// 演示多步骤验证链
/// </summary>
[TestMethod]
public void Scenario_ValidateUserInput()
{
// Act: 验证有效输入
var validResult = ValidateEmail("user@example.com");
// Assert
Assert.IsTrue(validResult.IsSuccess);
Assert.AreEqual("user@example.com", validResult.Value);
// Act: 验证无效输入
var invalidResult = ValidateEmail("invalid");
// Assert
Assert.IsTrue(invalidResult.IsFailure);
Assert.AreEqual("Invalid email format", invalidResult.ErrorMessage);
}
/// <summary>
/// 场景测试:数据库操作链
/// 演示如何使用 Result 处理可能失败的操作序列
/// </summary>
[TestMethod]
public void Scenario_DatabaseOperationChain()
{
// Act: 成功的操作链
var successResult = FindUserById(1)
.Bind(user => UpdateUserName(user, "NewName"))
.Bind(user => SaveUser(user))
.Map(user => $"User {user.Name} saved successfully");
// Assert
Assert.IsTrue(successResult.IsSuccess);
Assert.AreEqual("User NewName saved successfully", successResult.Value);
// Act: 失败的操作链(用户不存在)
var notFoundResult = FindUserById(999)
.Bind(user => UpdateUserName(user, "NewName"))
.Bind(user => SaveUser(user))
.Map(user => $"User {user.Name} saved successfully");
// Assert
Assert.IsTrue(notFoundResult.IsFailure);
Assert.IsTrue(notFoundResult.HasErrorCode(404L));
}
/// <summary>
/// 场景测试:错误恢复
/// 演示使用 OrElse 提供备用逻辑
/// </summary>
[TestMethod]
public void Scenario_ErrorRecovery()
{
// Act: 主操作失败,使用备用操作
var result = TryPrimaryOperation()
.OrElse(() => TryFallbackOperation())
.OrElse(() => Result.Success("Default value"));
// Assert
Assert.IsTrue(result.IsSuccess);
Assert.AreEqual("Fallback success", result.Value);
}
#endregion
#region 测试辅助方法
private Result<string> ValidateEmail(string email)
{
return email.AsResult()
.Ensure(e => !string.IsNullOrEmpty(e), "Email cannot be empty")
.Ensure(e => e.Contains("@"), "Invalid email format")
.Ensure(e => e.Length >= 5, "Email too short");
}
private class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
}
private Result<User> FindUserById(int id)
{
return id == 1
? Result.Success(new User { Id = 1, Name = "John" })
: Result.Failure<User>(404L, "User not found");
}
private Result<User> UpdateUserName(User user, string newName)
{
user.Name = newName;
return Result.Success(user);
}
private Result<User> SaveUser(User user)
{
// 模拟保存成功
return Result.Success(user);
}
private Result<string> TryPrimaryOperation()
{
return Result.Failure<string>("Primary operation failed");
}
private Result<string> TryFallbackOperation()
{
return Result.Success("Fallback success");
}
#endregion
}