如何使用NSubstitute模拟受保护的方法

9
public static void Mock(out IProgram prog, out IJson json)
{    
    prog = Substitute.For<IProgram>();
    IJson = Substitute.For<IJson>();

    prog.SaveProg(1, 1, 1, "Somthing", 1, DateTime.UtcNow,
                 (DateTime.UtcNow + TimeSpan.FromDays(10)), 
                 10, "someemail@email.com", DateTime.UtcNow, 1)
        .Returns(ObjectResult<int?>); 
}

当调用Returns(ObjectResult<int?>)时,我遇到了一个错误,因为ObjectResult是受保护的类。我该如何解决这个问题,以便能够从实际方法中调用我的模拟方法?


感谢Arturo的编辑。 - Karlen M
2
编造的例子的问题在于,如果你不检查它们,它们比毫无意义的还要糟糕。你的问题有很多问题需要解决。你给出的代码示例无法编译。.Returns期望一个实例,而不是一个类型。你目前正在模拟接口,这些接口不能有受保护的方法。ObjectResult<T>不是受保护的类(一个类需要嵌套才能受保护)。ObjectResult<T>有不同的版本,其中一些是密封的,一些有受保护的构造函数。你想使用哪个版本? - forsvarir
1
为什么您关心您无法看到的类的结果是什么?您能以一种基于ObjectResult<int?>结果的方式进行测试,以便您不必触及该类吗? - Timothy Gonzalez
2个回答

12

NSubstitute可以在你调用一个替代对象的方法后,覆盖该方法的行为。但实际上,它并不关心您是如何调用该方法的。这使您可以通过反射来调用该方法。

下面是一个非常详细的例子:

public class SomeRepository
{
    public string ReadData() => ActuallyPerformDataReading();
    protected virtual string ActuallyPerformDataReading() => "some wrong data";
}

public class SomeClass
{
    SomeRepository _someRepository;
    public SomeClass(SomeRepository someRepository)
    {
        _someRepository = someRepository;
    }

    public string ReadSomething() => _someRepository.ReadData();
}


var repositorySub = Substitute.For<SomeRepository>();
repositorySub.GetType().GetMethod("ActuallyPerformDataReading", BindingFlags.NonPublic | BindingFlags.Instance)
    .Invoke(repositorySub, new object[] {}).Returns("some test data");
var sut = new SomeClass(repositorySub);

var result = sut.ReadSomething(); //"some test data"

感谢这个简单明了的解决方案。最好能够以一种被认为是hackish的方式测试代码,而不是根本不进行测试(当您无法更改相关代码时)! - Jean-Sébastien Tremblay

-4

你不应该能够模拟受保护的类/方法。它是明确受保护的,所以你不能这样做。如果你需要模拟它,请将其设置为公共的。如果这是别人的方法,而你认为你需要模拟它,那么你可能在错误地进行测试。

编辑:受保护方法中的任何功能只能由同一类中的公共方法使用。模拟该公共方法以根据所需结果从受保护方法中进行操作。


3
除了我已经了解的知识,你还有什么建议吗?我无法更改访问修饰符。 - Karlen M
2
方法通常不会被标记为受保护的,以明确防止模拟/防止测试替身的创建。在某些情况下,例如ObjectResult<T>,会特别添加受保护的构造函数以便于创建测试替身。https://msdn.microsoft.com/en-us/library/mt154818(v=vs.113).aspx#M:System.Data.Entity.Core.Objects.ObjectResult`1。 - forsvarir
如果不允许模拟受保护的方法,那为什么要有privateprotected呢?这实际上是测试框架的一个限制,因为我可以扩展该类并模拟出受保护的方法。如果真的打算进行修改,它应该有virtual关键字,但我仍然可以进行模拟。为了创建一个测试替身而做所有这些事情,会导致非常冗长的代码,而我期望一个模拟框架能够减少这种复杂性。 - Timothy Gonzalez
我们可以从测试项目中继承类,并将方法/属性导出为公共的,这样就可以在内部获取/设置/访问受保护的成员了吗? - Brij

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