通过在 C# 中使用 Moq 模拟外部 DLL 进行单元测试
Unit test by mocking external DLL using Moq in C#
我正在尝试为我的一个依赖于外部 DLL 的函数编写单元测试。我决定使用 Moq(Mocking to external dependency)来测试我的功能。
MyClass 有一个函数和一个外部依赖项。
namespace MyService
{
/// <summary>
/// MyClass
/// </summary>
public class MyClass : IMyClass
{
private ExternalClass ex;
public MyClass() {
ex = new ExternalClass();
}
public bool publish(string param) {
if(... param is not valid)
throw error;
return ex.externalPublish(param);
}
}
}
外部 class 发布函数调用了一些 运行 时间依赖性,因此在单元测试期间 运行 无法预期函数
namespace ExternalService
{
/// <summary>
/// ExternalClass
/// </summary>
public class ExternalClass : IExternalClass, OtherExternalClass // OtherExternalClass is a concrete one, not an interface
{
public ExternalClass(){}
public bool publish(string param) {
// some implementation which contacts database, other running services etc.
}
}
}
namespace MyServiceTests
{
using Moq;
/// <summary>
/// MyClass test
/// </summary>
[TestClass]
public class MyClassTest
{
/// <summary>
/// Publish
/// </summary>
[TestMethod]
public void PublishTestSucceed()
{
var moq = new Mock<ExternalClass>(); // I tried IExternalClass but it is not the only inheritence that ExternalClass has.
moq.CallBase = true;
moq.Setup(tsmv2 => tsmv2.publish(
It.IsAny<string>()))
.Returns(true);
MyClass myC = new MyClass();
PrivateObject obj = new PrivateObject(myC);
obj.SetField("ex", moq.Object); // If I use IExternalClass to create moq, I am not able to convert IExternalClass to ExternalClass here.
bool result = myC.publish("asdf");
Assert.IsTrue(result);
}
所以有两个问题:
- 外部class并不完全依赖于一个接口。我还能在这里使用 Moq 库吗?
- 即使我使用接口,我也无法将
moq.Object
转换为 IExternalClass 对象,即使使用显式转换也是如此。
被测对象与具体的外部实现细节紧密耦合,应该依赖于通过构造函数注入使用显式依赖原则的抽象。
重构主题class:
public class MyClass : IMyClass {
private IExternalClass ex;
public MyClass(IExternalClass ex) {
this.ex = ex;
}
public bool publish(string param) {
if(... param is not valid)
throw error;
return ex.publish(param); //... external call
}
}
对于主题 class 来说,依赖是外部的,这真的应该无关紧要。那是一个实现细节。
这现在允许主题 class 明确说明它需要什么来执行其功能,并且通过依赖抽象允许使用替代品。例如,在隔离测试时。
[TestClass]
public class MyClassTest {
public void PublishTestSucceed() {
// Arrange
var dependency = new Mock<IExternalClass>();
dependency
.Setup(_ => _.publish(It.IsAny<string>()))
.Returns(true);
MyClass myC = new MyClass(dependency.Object);
//Act
bool result = myC.publish("asdf");
//Assert
Assert.IsTrue(result);
}
}
如果明确使用 IExternalClass
因为依赖项有太多限制,那么请考虑使用 encapsulates/wraps 外部依赖项功能的实现创建自己的抽象。
这样你就可以更好地控制你的代码,而不会受到你无法控制的依赖项的限制。
我正在尝试为我的一个依赖于外部 DLL 的函数编写单元测试。我决定使用 Moq(Mocking to external dependency)来测试我的功能。
MyClass 有一个函数和一个外部依赖项。
namespace MyService
{
/// <summary>
/// MyClass
/// </summary>
public class MyClass : IMyClass
{
private ExternalClass ex;
public MyClass() {
ex = new ExternalClass();
}
public bool publish(string param) {
if(... param is not valid)
throw error;
return ex.externalPublish(param);
}
}
}
外部 class 发布函数调用了一些 运行 时间依赖性,因此在单元测试期间 运行 无法预期函数
namespace ExternalService
{
/// <summary>
/// ExternalClass
/// </summary>
public class ExternalClass : IExternalClass, OtherExternalClass // OtherExternalClass is a concrete one, not an interface
{
public ExternalClass(){}
public bool publish(string param) {
// some implementation which contacts database, other running services etc.
}
}
}
namespace MyServiceTests
{
using Moq;
/// <summary>
/// MyClass test
/// </summary>
[TestClass]
public class MyClassTest
{
/// <summary>
/// Publish
/// </summary>
[TestMethod]
public void PublishTestSucceed()
{
var moq = new Mock<ExternalClass>(); // I tried IExternalClass but it is not the only inheritence that ExternalClass has.
moq.CallBase = true;
moq.Setup(tsmv2 => tsmv2.publish(
It.IsAny<string>()))
.Returns(true);
MyClass myC = new MyClass();
PrivateObject obj = new PrivateObject(myC);
obj.SetField("ex", moq.Object); // If I use IExternalClass to create moq, I am not able to convert IExternalClass to ExternalClass here.
bool result = myC.publish("asdf");
Assert.IsTrue(result);
}
所以有两个问题:
- 外部class并不完全依赖于一个接口。我还能在这里使用 Moq 库吗?
- 即使我使用接口,我也无法将
moq.Object
转换为 IExternalClass 对象,即使使用显式转换也是如此。
被测对象与具体的外部实现细节紧密耦合,应该依赖于通过构造函数注入使用显式依赖原则的抽象。
重构主题class:
public class MyClass : IMyClass {
private IExternalClass ex;
public MyClass(IExternalClass ex) {
this.ex = ex;
}
public bool publish(string param) {
if(... param is not valid)
throw error;
return ex.publish(param); //... external call
}
}
对于主题 class 来说,依赖是外部的,这真的应该无关紧要。那是一个实现细节。
这现在允许主题 class 明确说明它需要什么来执行其功能,并且通过依赖抽象允许使用替代品。例如,在隔离测试时。
[TestClass]
public class MyClassTest {
public void PublishTestSucceed() {
// Arrange
var dependency = new Mock<IExternalClass>();
dependency
.Setup(_ => _.publish(It.IsAny<string>()))
.Returns(true);
MyClass myC = new MyClass(dependency.Object);
//Act
bool result = myC.publish("asdf");
//Assert
Assert.IsTrue(result);
}
}
如果明确使用 IExternalClass
因为依赖项有太多限制,那么请考虑使用 encapsulates/wraps 外部依赖项功能的实现创建自己的抽象。
这样你就可以更好地控制你的代码,而不会受到你无法控制的依赖项的限制。