问题
I have the following class:
public class Foo
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public Stream TestStream;
public WriteToTestStream()
{ ... }
}
WriteToTestStream
writes to the TestStream
.
I want to write a Unit Test to make sure that
Prop1
andProp2
are being set before writing to theTestStream
- The properties should not be accessed once something is written to the
TestStream
.
How can I define a Mock for WriteToTestStream
in this case?
How can I manipulate local variables from a Mocked function(I can use local variables in the Unit Test to remember when the Stream was written / properties were set)?
回答1:
One way to do this is by keeping local state variables and then using Moq's callbacks to track the state, you can Assert
within the callbacks to ensure the correct state. Here's an example (Albeit in NUnit). I've also assumed that you actually want to test a SUT which uses Foo
in the correct order (I've called the SUT Bar
here).
[TestFixture]
public class UnitTest
{
private bool _prop1Set = false;
private bool _prop2Set = false;
private Mock<IFoo> _mockFoo;
[SetUp]
public void Setup()
{
_mockFoo = new Mock<IFoo>();
_mockFoo.SetupSet(m => m.Prop1).Callback(i => _prop1Set = true);
_mockFoo.SetupSet(m => m.Prop2).Callback(s => _prop2Set = true);
_mockFoo.Setup(m => m.WriteToTestStream())
.Callback(() => Assert.IsTrue(_prop1Set && _prop2Set));
}
[Test]
public void EnsureInOrder()
{
var sut = new Bar(_mockFoo.Object);
Assert.DoesNotThrow(() => sut.DoSomethingInOrder());
}
[Test]
public void EnsureOutOfOrder()
{
var sut = new Bar(_mockFoo.Object);
Assert.Catch<Exception>(() => sut.DoSomethingOutOfOrder());
}
}
Note that in order to Mock your Foo
class, methods and properties need to be overridable, e.g. all virtual or with an abstracted interface.
Here's an example of such refactoring:
public interface IFoo
{
int Prop1 { get; set; }
string Prop2 { get; set; }
void WriteToTestStream();
}
public class Foo : IFoo
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
// Surely this is internal implementation
private Stream TestStream;
public void WriteToTestStream() { }
}
public class Bar
{
private readonly IFoo _foo;
public Bar(IFoo injectedFoo)
{
_foo = injectedFoo;
}
public void DoSomethingInOrder()
{
_foo.Prop1 = 1;
_foo.Prop2 = "Baz";
_foo.WriteToTestStream();
}
public void DoSomethingOutOfOrder()
{
_foo.WriteToTestStream();
_foo.Prop2 = "Baz";
_foo.Prop1 = 1;
}
}
Another way of doing this could be to use Declan Whelan's MoqSequences extension, although I must admit I haven't used it yet.
Edit
It appears that MockSequence
(InSequence
) has made it into recent versions of Moq, although this might enforce a very rigid sequence, and also appears to require MockBehavior.Strict:
[TestFixture]
public class UnitTest
{
private Mock<IFoo> _mockFoo;
[SetUp]
public void Setup()
{
_mockFoo = new Mock<IFoo>(MockBehavior.Strict);
var seq = new MockSequence();
_mockFoo.InSequence(seq).SetupSet(m => m.Prop1 = It.IsAny<int>());
_mockFoo.InSequence(seq).SetupSet(m => m.Prop2 = It.IsAny<string>());
_mockFoo.InSequence(seq).Setup(m => m.WriteToTestStream());
}
[Test]
public void EnsureInOrder()
{
var sut = new Bar(_mockFoo.Object);
Assert.DoesNotThrow(() => sut.DoSomethingInOrder());
}
[Test]
public void EnsureOutOfOrder()
{
var sut = new Bar(_mockFoo.Object);
Assert.Catch<MockException>(() => sut.DoSomethingOutOfOrder());
}
}
来源:https://stackoverflow.com/questions/24027301/how-to-verify-order-of-execution-in-xunit-moq