Configure Mock Objects' method
1). the behavior of a method is deterministic, then its result is determined by its inputs
2). the behavior of a method is non-deterministic, then its result is random.
3). the behavior of a method is stateful, meaning also related to the number of calls made on it.
A result can be a value or an exceptions. Moreover, in C#, result can also be async.
** Because it is mock object, usually just need hardcode. No if-else logic required"
MockObject.Setup takes an input of expression of TResult Fun(T)
returns a ISetup<T, TResult>
ISetup has methods:
1). a family of Returns: IReturnsResult
ISetup has extension methods:
1). a family of ReturnsAsync, ThrowsAsync, which can also have time-delay argument: IReturnsResult
Mock<T> {.Setup<T, TResult>(Expression<Func<T, TResult>> )}
| |----------| |
| | |
Mock IMock<T> {T Object} |
returns
|
\/
ISetup<T, TResult>
|
|
IReturns<T, TResult> { Returns(TResult ), ReturnsAsync(TResult )}
| |
----------------
|
IReturnsResult<T>
MockObject.SetupSequential takes an input of expression of TResult Fun(T)
returns a ISetupSequentialResult
ISetupSequentialResult has extension methods:
1). ReturnsAsync, 2). ThrowsAsync, both of them also return a ISetupSequentialResult, so it's chainable.
# deterministic stateless.
mock.Setup(foo => foo.DoSomething(It.IsAny<string>())).Returns(true);
mock.Setup(foo => foo.DoSomething("hello")).Returns(true);
// other input condition can be specified by a range, by a lambda.
// non-deterministic
mock.Setup(foo => foo.DoSomething("hello")).Returns((New Random()).Next(1000));
// throws exceptions
mock.Setup(foo => foo.DoSomething("hello"))
.Throws<InvalidOperationException>();
mock.Setup(foo => foo.DoSomething("hi"))
.Throws(new ArgumentException("command"));
// stateful
// 1st call returns "10", 2nd call returns "11", 3rd call throws error.
formatterMock.SetupSequence(x => x.Format(It.IsAny<int>()))
.Returns("10")
.Returns("11");
// 1st call returns "10", 2nd call returns "11", 3rd call throws exception.
formatterMock.SetupSequence(x => x.Format(It.IsAny<int>()))
.Returns("10")
.Returns("11")
.Throws(new ArgumentException("command"));
sequential asycn
Initialize target objects with Autofac
The simple way is to create Mock object and initialize a target object by using its constructor. Another approach is to use Autofac Dependency Injection to inject Mock Object.
Strict mocking: If the test method invokes that method or property and the Mock object does not set them, then it will throw exceptions.
Loose mocking: If the test method invokes that method or property and the Mock object does not set them, then you get the default value for that operation (which mostly will be a zero or a null). For example, calling a method "string Format(int i)" will get null.
using Autofac.Extras.Moq;
using Moq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Service;
using Targets;
namespace targets.tests
{
[TestClass]
public class AnotherTargetTests
{
private AutoMock _mock; // a container
private Target _target; // target object.
[TestInitialize]
public void Init()
{
_mock = AutoMock.GetLoose();
_target = _mock.Create<Target>();
}
[TestMethod]
public void Print_WithNumberArray_ShouldReturnString()
{
/*
* var formatterMock = new Mock<IFormatter>();
* formatterMock.Setup(x => x.Format(It.IsAny<int>()))
.Returns("11");
*/
// register and configure dependencies
_mock.Mock<IFormatter>()
.Setup(x => x.Format(It.IsAny<int>()))
.Returns("11");
// var target = new Target(formatterMock.Object);
var result = _target.Print(0, 2, 3);
Assert.AreEqual("111111", result);
_mock.Mock<IFormatter>().Verify(x => x.Format(It.IsAny<int>()), Times.Exactly(3));
Assert.AreEqual(0, It.IsAny<int>());
}
[TestMethod]
public void Print_WithNumberArray_ShouldReturnString2()
{
// even though we still use the same container, but dependencies are reset!!!!
var result = _target.Print(0, 2, 3);
Assert.AreEqual("", result);
_mock.Mock<IFormatter>().Verify(x => x.Format(It.IsAny<int>()), Times.Exactly(3));
Assert.AreEqual(0, It.IsAny<int>());
}
}
}
Because container is reset for each test method, it is better to use the following manner.
[TestMethod]
public void Print_WithNumberArray_ShouldReturnString()
{
using (var mock = AutoMock.GetLoose())
{
var formatterMock = mock.Mock<IFormatter>();
formatterMock.Setup(x => x.Format(It.IsAny<int>()))
.Returns("13");
var target = mock.Create<Target>(new NamedParameter("formatter", formatterMock.Object)); // with a designated parameter.
var result = target.Print(0, 2, 3);
Assert.AreEqual("131313", result);
mock.Mock<IFormatter>().Verify(x => x.Format(It.IsAny<int>()), Times.Exactly(3));
}
}
** Target objects can be .Create before registering dependencies. When dependencies is registered, the behavior of target will be modified. ***