我正在编写一个小的Functional C#库,在其中定义了Unit
`类型以及一堆扩展方法以进行转换:
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;
}
我想知道如何对这些扩展方法进行单元测试。
我应该进行某种类型的反射来分析Func
的返回类型和参数,是否应该在调用Func
时检查是否调用了基础Action
方法?
有什么想法吗?
答案 0 :(得分:3)
最简单的方法是传递一个动作,该动作在调用该动作时将布尔值设置为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();
}
在上面
func
是否为空。测试func()
的结果可以解决这种情况。噪音更少。我会跳过所有思考。我怀疑被测方法的数量是否足够少,以至于可以重复执行一些代码,因为它比反射所需的扭曲更具可读性。
答案 1 :(得分:2)
使用类似NSubstitute的模拟框架,您可以轻松断言已调用了给定的方法/操作,并且所传递的参数的值符合预期。
在下面的示例中,我使用的是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);
}
}