如何设置从静态方法返回的对象?单元测试
How to setup returned object from static method? UnitTesting
假设我有一个方法A
public class ClassWithAMethod
{
public static void A(string email)
{
var b = SomeClass.StaticMethod();
var c = b.GetUser(x => x.Email == email);
if (c != null)
{
throw new Exception();
}
}
}
我需要测试一个调用方法 A
的方法 Abc
。
例如
public void Abc()
{
ClassWithAMethod.A("123@gmail.com");
}
所以我需要模拟对象 b
和设置方法 GetUser
但我该怎么做?
如果不修改,我认为这是不可能的。
一个轻微的修改和非破坏性的变化可能是添加一个可选参数 bGetter
来获取 b。
然后,您可以在单元测试中创建自己的 bGetter
。
public class ClassWithAMethod
{
public static void A(string email, Func<B> bGetter = null)
{
var b = null == bGetter ? SomeClass.StaticMethod() : bGetter();
var c = b.GetUser(x => x.Email == email);
if (c != null)
{
throw new Exception();
}
}
}
俗话说:你可以用另一个间接层解决所有问题,除了间接层太多的问题 Reference
通过引入包装器/适配器,您的解决方案变得更加loosely coupled。
合同
那么,首先让我们介绍一下下面的界面:
public interface IBFactory
{
B Create();
}
包装器
让我们创建包装器:
public class BWrapper: IBFactory
{
public B Create()
{
return SomeClass.StaticMethod();
}
}
备选
在 C# 8 的情况下,您可以依靠 default implementation in interface 功能将前两个步骤合二为一:
public interface IBFactory
{
public B Create()
{
return SomeClass.StaticMethod();
}
}
依赖抽象
现在让我们修改 ClassWithAMethod
class 以依赖 IBFactory
:
public class ClassWithAMethod
{
private readonly IBFactory _bFactory;
public ClassWithAMethod(IBFactory bFactory)
{
this._bFactory = bFactory;
}
public static void A(string email)
{
var b = this._bFactory.Create();
var c = b.GetUser(x => x.Email == email);
if (c != null)
{
throw new Exception();
}
}
}
您可以在 DI 容器中将 BWrapper
注册为 IBFactory
的默认实现。或者,如果您既不使用 DI 也不使用 C# 8,那么您可以在依赖 BWrapper
的地方指定一个无参数构造函数。请记住,不建议使用这种方法。这更像是最后的手段。
public class ClassWithAMethod
{
private readonly IBFactory _bFactory;
public ClassWithAMethod()
{
this._bFactory = new BWrapper();
}
public ClassWithAMethod(IBFactory bFactory)
{
this._bFactory = bFactory;
}
...
}
单元测试
现在您也可以模拟该依赖关系。
var bFactoryMock = new Mock<IBFactory>();
bFactoryMock.Setup(factory => factory.Create()).Returns(...);
var SUT = new ClassWithAMethod(bFactoryMock.Object);
假设我有一个方法A
public class ClassWithAMethod
{
public static void A(string email)
{
var b = SomeClass.StaticMethod();
var c = b.GetUser(x => x.Email == email);
if (c != null)
{
throw new Exception();
}
}
}
我需要测试一个调用方法 A
的方法 Abc
。
例如
public void Abc()
{
ClassWithAMethod.A("123@gmail.com");
}
所以我需要模拟对象 b
和设置方法 GetUser
但我该怎么做?
如果不修改,我认为这是不可能的。
一个轻微的修改和非破坏性的变化可能是添加一个可选参数 bGetter
来获取 b。
然后,您可以在单元测试中创建自己的 bGetter
。
public class ClassWithAMethod
{
public static void A(string email, Func<B> bGetter = null)
{
var b = null == bGetter ? SomeClass.StaticMethod() : bGetter();
var c = b.GetUser(x => x.Email == email);
if (c != null)
{
throw new Exception();
}
}
}
俗话说:你可以用另一个间接层解决所有问题,除了间接层太多的问题 Reference
通过引入包装器/适配器,您的解决方案变得更加loosely coupled。
合同
那么,首先让我们介绍一下下面的界面:
public interface IBFactory
{
B Create();
}
包装器
让我们创建包装器:
public class BWrapper: IBFactory
{
public B Create()
{
return SomeClass.StaticMethod();
}
}
备选
在 C# 8 的情况下,您可以依靠 default implementation in interface 功能将前两个步骤合二为一:
public interface IBFactory
{
public B Create()
{
return SomeClass.StaticMethod();
}
}
依赖抽象
现在让我们修改 ClassWithAMethod
class 以依赖 IBFactory
:
public class ClassWithAMethod
{
private readonly IBFactory _bFactory;
public ClassWithAMethod(IBFactory bFactory)
{
this._bFactory = bFactory;
}
public static void A(string email)
{
var b = this._bFactory.Create();
var c = b.GetUser(x => x.Email == email);
if (c != null)
{
throw new Exception();
}
}
}
您可以在 DI 容器中将 BWrapper
注册为 IBFactory
的默认实现。或者,如果您既不使用 DI 也不使用 C# 8,那么您可以在依赖 BWrapper
的地方指定一个无参数构造函数。请记住,不建议使用这种方法。这更像是最后的手段。
public class ClassWithAMethod
{
private readonly IBFactory _bFactory;
public ClassWithAMethod()
{
this._bFactory = new BWrapper();
}
public ClassWithAMethod(IBFactory bFactory)
{
this._bFactory = bFactory;
}
...
}
单元测试
现在您也可以模拟该依赖关系。
var bFactoryMock = new Mock<IBFactory>();
bFactoryMock.Setup(factory => factory.Create()).Returns(...);
var SUT = new ClassWithAMethod(bFactoryMock.Object);