如何对将 Action 转换为 Func 的扩展方法进行单元测试?
How to unit test an extension method converting an Action to a Func?
我正在编写一个小型 C# 函数库,其中定义了一个 Unit
`type 以及一堆要转换的扩展方法:
Action
Action<T1>
Func<T1, T2>
- 等
收件人:
Func<Unit>
Func<T1, Unit>
Func<T1,T2, Unit>
- 等等
public static partial class ActionExtensions
{
public static Func<Unit> ToFunc(this Action action) =>
() =>
{
action();
return Unit.Default;
};
public static Func<T, Unit> ToFunc<T>(this Action<T> action) =>
t =>
{
action(t);
return Unit.Default;
};
public static Func<T1, T2, Unit> ToFunc<T1, T2>(this Action<T1, T2> action) =>
(t1, t2) =>
{
action(t1, t2);
return Unit.Default;
};
// other methods...
Unit
定义如下:
public readonly struct Unit : IEquatable<Unit>
{
public static readonly Unit Default = new Unit();
public override int GetHashCode() =>
0;
public override bool Equals(object obj) =>
obj is Unit;
public bool Equals(Unit other) =>
true;
public static bool operator ==(Unit left, Unit right) =>
true;
public static bool operator !=(Unit left, Unit right) =>
false;
}
我想知道如何对这些扩展方法进行单元测试。
我是否应该执行某种反射来分析 return 类型以及 Func
的参数,我是否应该检查在调用 Func
时调用底层 Action
方法?
有什么想法吗?
最简单的方法是传递一个在调用操作时将布尔值设置为 true 的操作,然后测试扩展方法的结果。这证明了预期的行为,即
- 动作被调用
- 函数以默认单位返回
例如,
[Test] public void ToFuncForGivenActionInvokesAction()
{
// arrange
bool invoked = false;
Action action = () => invoked = true;
// act
var func = action.ToFunc();
// assert
func().Should().Be(Unit.Default);
invoked.Should().BeTrue();
}
在上面
- 我正在使用 Fluent Assertions 来断言证据,但你当然可以使用任何你想要的
- 不必费心检查
func
是否为空。测试 func()
的结果涵盖了这种情况。减少噪音。
- 对带参数的操作重复。
我会跳过所有的反思。我怀疑被测方法的数量足够少,以至于可以重复一些代码,因为这比反射所需的扭曲更具可读性。
使用像 NSubstitute 这样的模拟框架,您可以轻松断言给定的 method/action 已被调用,并且传递的参数的值符合预期。
在下面的示例中,我使用的是 xUnit 测试框架。
断言 testDummy.Received().Run("foobar", 123);
如果
将无法通过测试
Run
方法还没有执行
Run
方法会收到除 foobar
和 123
. 之外的其他参数值
public interface ITestDummy
{
void Run(String arg1, Int32 arg2);
}
public class UnitTest
{
[Fact]
public void Test1()
{
var testDummy = Substitute.For<ITestDummy>();
Action<String, Int32> action = testDummy.Run;
Func<String, Int32, Unit> func = action.ToFunc();
var result = func("foobar", 123);
testDummy.Received().Run("foobar", 123);
Assert.Equal(result, Unit.Default);
}
}
我正在编写一个小型 C# 函数库,其中定义了一个 Unit
`type 以及一堆要转换的扩展方法:
Action
Action<T1>
Func<T1, T2>
- 等
收件人:
Func<Unit>
Func<T1, Unit>
Func<T1,T2, Unit>
- 等等
public static partial class ActionExtensions
{
public static Func<Unit> ToFunc(this Action action) =>
() =>
{
action();
return Unit.Default;
};
public static Func<T, Unit> ToFunc<T>(this Action<T> action) =>
t =>
{
action(t);
return Unit.Default;
};
public static Func<T1, T2, Unit> ToFunc<T1, T2>(this Action<T1, T2> action) =>
(t1, t2) =>
{
action(t1, t2);
return Unit.Default;
};
// other methods...
Unit
定义如下:
public readonly struct Unit : IEquatable<Unit>
{
public static readonly Unit Default = new Unit();
public override int GetHashCode() =>
0;
public override bool Equals(object obj) =>
obj is Unit;
public bool Equals(Unit other) =>
true;
public static bool operator ==(Unit left, Unit right) =>
true;
public static bool operator !=(Unit left, Unit right) =>
false;
}
我想知道如何对这些扩展方法进行单元测试。
我是否应该执行某种反射来分析 return 类型以及 Func
的参数,我是否应该检查在调用 Func
时调用底层 Action
方法?
有什么想法吗?
最简单的方法是传递一个在调用操作时将布尔值设置为 true 的操作,然后测试扩展方法的结果。这证明了预期的行为,即
- 动作被调用
- 函数以默认单位返回
例如,
[Test] public void ToFuncForGivenActionInvokesAction()
{
// arrange
bool invoked = false;
Action action = () => invoked = true;
// act
var func = action.ToFunc();
// assert
func().Should().Be(Unit.Default);
invoked.Should().BeTrue();
}
在上面
- 我正在使用 Fluent Assertions 来断言证据,但你当然可以使用任何你想要的
- 不必费心检查
func
是否为空。测试func()
的结果涵盖了这种情况。减少噪音。 - 对带参数的操作重复。
我会跳过所有的反思。我怀疑被测方法的数量足够少,以至于可以重复一些代码,因为这比反射所需的扭曲更具可读性。
使用像 NSubstitute 这样的模拟框架,您可以轻松断言给定的 method/action 已被调用,并且传递的参数的值符合预期。
在下面的示例中,我使用的是 xUnit 测试框架。
断言 testDummy.Received().Run("foobar", 123);
如果
Run
方法还没有执行Run
方法会收到除foobar
和123
. 之外的其他参数值
public interface ITestDummy
{
void Run(String arg1, Int32 arg2);
}
public class UnitTest
{
[Fact]
public void Test1()
{
var testDummy = Substitute.For<ITestDummy>();
Action<String, Int32> action = testDummy.Run;
Func<String, Int32, Unit> func = action.ToFunc();
var result = func("foobar", 123);
testDummy.Received().Run("foobar", 123);
Assert.Equal(result, Unit.Default);
}
}