NSubstitute ForPartsOf...当调用真正的方法时
NSubstitute ForPartsOf...When calls real method
我正在尝试使用 ForPartsOf<...>()
替代方法,然后使用 subst.Configure().MyMethod(...).Returns(...)
或 subst.When(x => x.MyMethod(..)).Returns(...)
,但在这两种情况下都会调用真正的 MyMethod
。我的印象是 Configure()
和 When()
都应该确保 MyMethod()
调用是在 "configure mode" 中进行的,因此不会进行真正的调用。我错了吗?还是我做错了什么?
下面是我的(大大简化并更改了名称的)代码。对于 subst1
和 subst2
,真正的 NeedsMoreWork
方法是用 item == null
.
调用的
public interface IMyClass
{
bool NeedsMoreWork(Item item, out Part part);
bool DoWork(Item item);
}
public class MyClass : IMyClass
{
private ILogger log;
public MyClass(ILogger log)
{
this.log = log;
}
public bool NeedsMoreWork(Item item, out Part part)
{
log.Debug($"Examining item {item.Id}");
part = null;
if (item.Completed())
{
log.Debug($"Item {item.Id} already completed.");
return false;
}
part = item.GetNextPart();
log.Debug($"Item {item.Id} needs work on part {part.Id}.");
return true;
}
public bool DoWork(Item item)
{
if (!item.NeedsMoreWork(item, out Part part))
return false;
log.Debug($"Starting work on part {part.Id}.");
// Do work on part.
log.Debug($"Work completed on part {part.Id}.");
return true;
}
}
[TestMethod]
public void TestDoWork()
{
// Version with Configure():
var subst1 = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
subst1.Configure()
.NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>())
.Returns(false);
// Version with WhenForAnyArgs():
var subst2 = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
subst2.WhenForAnyArgs(x => x.NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>())
.Returns(false);
}
使用 NSubstitute 时,要成为 mocked/stubbed 的成员需要成为 virtual
才能被覆盖。
引用Partial subs and test spies
public class MyClass : IMyClass {
private readonly ILogger log;
public MyClass(ILogger log) {
this.log = log;
}
public virtual bool NeedsMoreWork(Item item, out Part part) { //<-- THIS IS NOW VIRTUAL
log.Debug($"Examining item {item.Id}");
part = null;
if (item.Completed()) {
log.Debug($"Item {item.Id} already completed.");
return false;
}
part = item.GetNextPart();
log.Debug($"Item {item.Id} needs work on part {part.Id}.");
return true;
}
public bool DoWork(Item item) {
if (!NeedsMoreWork(item, out Part part))
return false;
log.Debug($"Starting work on part {part.Id}.");
// Do work on part.
log.Debug($"Work completed on part {part.Id}.");
return true;
}
}
NeedsMoreWork
现在可以根据需要存根
[TestMethod]
public void TestDoWork_Should_Return_False() {
//Arrange
var subject = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
bool expected = false;
subject.Configure().NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>()).Returns(expected);
//Act
bool actual = subject.DoWork(new Item());
//Assert - FluentAssertions
actual.Should().Be(expected);
}
Note the CAUTION comment. If we had not used Configure()
here before .NeedsMoreWork(...)
then the real method would have executed before we had a chance to override the behavior. In some cases this may not be a problem, but if in doubt make sure you call Configure()
first so NSubstitute knows you are configuring a call and don’t want to run any real code. (This still does not guarantee real code will not run – remember, NSubstitute will not prevent non-virtual calls from executing.)
The Configure()
method is only available in NSubstitute 4.0 and above. For verisons prior to 4.0 we need to use When .. DoNotCallBase
注意:强调我的
我正在尝试使用 ForPartsOf<...>()
替代方法,然后使用 subst.Configure().MyMethod(...).Returns(...)
或 subst.When(x => x.MyMethod(..)).Returns(...)
,但在这两种情况下都会调用真正的 MyMethod
。我的印象是 Configure()
和 When()
都应该确保 MyMethod()
调用是在 "configure mode" 中进行的,因此不会进行真正的调用。我错了吗?还是我做错了什么?
下面是我的(大大简化并更改了名称的)代码。对于 subst1
和 subst2
,真正的 NeedsMoreWork
方法是用 item == null
.
public interface IMyClass
{
bool NeedsMoreWork(Item item, out Part part);
bool DoWork(Item item);
}
public class MyClass : IMyClass
{
private ILogger log;
public MyClass(ILogger log)
{
this.log = log;
}
public bool NeedsMoreWork(Item item, out Part part)
{
log.Debug($"Examining item {item.Id}");
part = null;
if (item.Completed())
{
log.Debug($"Item {item.Id} already completed.");
return false;
}
part = item.GetNextPart();
log.Debug($"Item {item.Id} needs work on part {part.Id}.");
return true;
}
public bool DoWork(Item item)
{
if (!item.NeedsMoreWork(item, out Part part))
return false;
log.Debug($"Starting work on part {part.Id}.");
// Do work on part.
log.Debug($"Work completed on part {part.Id}.");
return true;
}
}
[TestMethod]
public void TestDoWork()
{
// Version with Configure():
var subst1 = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
subst1.Configure()
.NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>())
.Returns(false);
// Version with WhenForAnyArgs():
var subst2 = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
subst2.WhenForAnyArgs(x => x.NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>())
.Returns(false);
}
使用 NSubstitute 时,要成为 mocked/stubbed 的成员需要成为 virtual
才能被覆盖。
引用Partial subs and test spies
public class MyClass : IMyClass {
private readonly ILogger log;
public MyClass(ILogger log) {
this.log = log;
}
public virtual bool NeedsMoreWork(Item item, out Part part) { //<-- THIS IS NOW VIRTUAL
log.Debug($"Examining item {item.Id}");
part = null;
if (item.Completed()) {
log.Debug($"Item {item.Id} already completed.");
return false;
}
part = item.GetNextPart();
log.Debug($"Item {item.Id} needs work on part {part.Id}.");
return true;
}
public bool DoWork(Item item) {
if (!NeedsMoreWork(item, out Part part))
return false;
log.Debug($"Starting work on part {part.Id}.");
// Do work on part.
log.Debug($"Work completed on part {part.Id}.");
return true;
}
}
NeedsMoreWork
现在可以根据需要存根
[TestMethod]
public void TestDoWork_Should_Return_False() {
//Arrange
var subject = Substitute.ForPartsOf<MyClass>(Substitute.For<ILogger>());
bool expected = false;
subject.Configure().NeedsMoreWork(Arg.Any<Item>(), out Arg.Any<Part>()).Returns(expected);
//Act
bool actual = subject.DoWork(new Item());
//Assert - FluentAssertions
actual.Should().Be(expected);
}
Note the CAUTION comment. If we had not used
Configure()
here before.NeedsMoreWork(...)
then the real method would have executed before we had a chance to override the behavior. In some cases this may not be a problem, but if in doubt make sure you callConfigure()
first so NSubstitute knows you are configuring a call and don’t want to run any real code. (This still does not guarantee real code will not run – remember, NSubstitute will not prevent non-virtual calls from executing.)The
Configure()
method is only available in NSubstitute 4.0 and above. For verisons prior to 4.0 we need to useWhen .. DoNotCallBase
注意:强调我的