NSubstitute:模拟静态类和静态方法

5
我是一名新手,正在进行单元测试,并尝试模拟静态类中的静态方法。我已经知道你不能这样做,但我在寻找一种解决方法。
我无法修改代码,将相同的函数设置为非静态也不是选项,因为它们检查测试的代码覆盖率,我需要至少90%。
我已经尝试过模拟它使用的变量,但它不起作用。
public static class MyClass
{
    public static response MyMethod(HttpSessionStateBase Session, 
        otherVariable, stringVariable)
    {
        //some code
    }
}

public ActionResult MyClassTested()
{
    var response = MyClass.MyMethod(Session);
    //more code
}

我的问题是,这个方法在一个控制器内部声明了一个响应变量,并根据该变量重定向用户。
2个回答

2
针对这种问题,可能有更好的解决方案...具体取决于你可以做些什么。
最近我自己遇到了这个问题,我编写了一个静态实用程序类,用于创建各种截断的 Guid 格式。在编写集成测试时,我意识到我需要控制从此实用程序类生成的随机 Id,以便我可以故意将此 Id 发布到 API,然后断言结果。
当时我选择的解决方案是提供来自静态类的实现,但从非静态类中调用该实现(包装静态方法调用),我可以在 DI 容器中注册和注入这个非静态类。这个非静态类将是主要工作马,但静态实现将在需要从另一个静态方法调用这些方法的情况下可用(例如,我已经将大量的集成设置代码编写为 IWevApplicationFactory 的扩展,并使用静态实用程序创建数据库名称)。
示例代码:
// my static implementation - only use this within other static methods when necessary. Avoid as much as possible.
public static class StaticGuidUtilities 
{
    public static string CreateShortenedGuid([Range(1, 4)] int take)
    {
        var useNumParts = (take > 4) ? 4 : take;
        var ids = Guid.NewGuid().ToString().Split('-').Take(useNumParts).ToList();
        return string.Join('-', ids);
    }
}


// Register this abstraction in the DI container and use it as the default guid utility class
public interface IGuidUtilities
{
    string CreateShortenedGuid([Range(1, 4)] int take);
}

// Non-static implementation
public class GuidUtitlities : IGuidUtilities
{
    public string CreateShortenedGuid([Range(1, 4)] int take)
    {
        return StaticGuidUtilities.CreateShortenedGuid(take);
    }
}

----

// In the tests, I can use NSubstitute...
// (Doesn't coding to the abstraction make our lives so much easier?)
var guidUtility = Substitute.For<IGuidUtilities>();
var myTestId = "test-123";
guidUtility.CreateShortenedGuid(1).Returns(myTestId);

// Execute code and assert on 'myTestId' 
// This method will call the injected non-static utilty class and return the id
var result = subjectUndertest.MethodUnderTest();

// Shouldly syntax
result.Id.ShouldBe(myTestId);

1
如果您无法修改代码,则我认为使用基于DynamicProxy的库(如NSubstitute)绕过这个问题是不可能的。这些库使用继承来拦截类成员,这对于静态和非虚拟成员是不可能的。
我建议尝试Fakes。该页面上的示例之一涵盖了桩造DateTime.Now
其他可以模拟静态成员的替代方案包括TypeMock和Telerik JustMock。
相关问题:https://dev59.com/B2025IYBdhLWcg3wr4B6

是的,我也看了那个,但我正在使用VS 2017专业版,所以我没有假对象,但似乎我将无法这样做,我必须使用其他东西或修改代码。 - Ricardo Sanchez Santos
@RicardoSanchezSantos 您可以尝试使用TypeMock或Telerik JustMock。我已经更新了答案以提及这些内容。大多数.NET模拟库通过继承拦截类上的调用,如此处所述(https://nsubstitute.github.io/help/how-nsub-works/),因此您需要选择另一个通过 profiling API 或类似方式工作的库。 - David Tchepak
有没有类似于 Typemock 或 Telerik 的免费工具? - Ricardo Sanchez Santos
我尝试安装Pose,但是我遇到了一个问题,框架=4.6.2与Pose不兼容。我也尝试了Smock,但我不太确定如何使用它,并且我正在调查Microsoft的moles。 - Ricardo Sanchez Santos
很抱歉我不确定价格。最好在Pose和Smock的项目网站上询问,或者在其他关于你遇到的具体问题的SO问题中提问。祝你好运! - David Tchepak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接