Introduction
Mocking technique is very useful for testing purpose. Moq is a third party framework that enables us to create the dummy class and dummy implementation. This is very useful in the scenarios where we want to test the functionality of a class without implementing the other classes and their methods.
Installation
-
create a service project and a unit test project
-
nuget > Moq > install
Steps
-
Create a mock object
var mockObj = new Mock<MockClass>()
-
Set up the mock object
two major methods
-
specific value
// set up the mock by specifying argument values mockObj.Setup(x => x.method(param1, ...)).Returns(xxx)
-
Moq’s argument matching technique
// specify the param of the mock should be a certain data type mockObj.Setup(x => x.method(It.IsAny<Param1DataType>(), ...)).Returns(xxx) // specify the param of the mock must meet some conditions mockObj.Setup(x => x.IsValid(It.Is<Param1DataType>(param1 => param1..condition...)).Returns(xxx) // specify the param of the mock must be within a set mockObj.Setup(x => x.IsValid(It.IsIn("xx", "yy", "zz"))).Returns(xxx) // specify the param of the mock must be within a range mockObj.Setup(x => x.IsValid(It.IsInRange("xx", "yy", "zz", Range.Inclusive))).Returns(xxx) // specify the param of the mock must meet a regex mockObj.Setup(x => x.IsValid(It.IsRegex("[xx|yy|zz]", RegexOptions.None))).Returns(xxx)
-
-
Inject the mock object via constructor or method parameter to business logic. Then, make the test
var result = BusinessServiceClassInstance.get(mockObj.Object); Assert.IsTrue(result); // mockObj.Verify(m => m.Method1(It.isAny<ParamDataType>()), Times.Once); mockObj.Verify(m => m.Method2(It.isAny<ParamDataType>()), Times.Exactly(2));
Demo 1 - Make uniting testing with mock via AAA principle
In service project
-
Develop some service logic
public interface IMailClient { string Server { get; set; } string Port { get; set; } bool SendMail(string from, string to, string subject, string body); } public interface IMailer { string From { get; set; } string To { get; set; } string Subject { get; set; } string Body { get; set; } bool SendMail(IMailClient mailClient); } // logic public class DefaultMailer : IMailer { public string From { get; set; } public string To { get; set; } public string Subject { get; set; } public string Body { get; set; } public bool SendMail(IMailClient mailClient) { return mailClient.SendMail(this.From, this.To, this.Subject, this.Body); } }
We defined two interfaces and interfaces is okay to mock. Moq will create mocks to make default
implementation for us.
In unit test project
-
Create a unit test class
-
Arrange - Create a mock object. Pass the mock to the logic
// create mock object var mockMailClient = new Mock<IMailClient>(); // setup the mock object, here we setup the SendMail() method mockMailClient.Setup(client => client.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()) );
-
Act- Execute the logic with mock
// create mock data var mailer = new DefaultMailer() { From = "from@mail.com", To = "to@mail.com", Subject = "Using Moq", Body = "Moq is awesome" }; // use mock to send email mailer.SendMail(mockMailClient.Object);
-
Assert - Verify the system interaction
// we expect client.SendMail() method is called at least once mockMailClient.Verify(client => client.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.AtLeastOnce);
The complete unit test code
[TestClass]
public class MoqTest
{
[TestMethod]
public void SendEmail_ShouldSucceed()
{
// create mock
var mockMailClient = new Mock<IMailClient>();
mockMailClient
.SetupProperty(client => client.Server, "server.mail.com")
.SetupProperty(client => client.Port, "1000");
// set up mock
mockMailClient.Setup(client =>
client.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())
)
.Callback(new Action<string, string, string, string>((a, b, c, d) =>
{
System.Console.WriteLine($"from: {a} -> to: {b}, subject: {c}, body: {d}");
}))
.Returns(true);
// create mock data
var mailer = new DefaultMailer() { From = "from@mail.com", To = "to@mail.com", Subject = "Using Moq", Body = "Moq is awesome" };
// use mock to send email
var result = mailer.SendMail(mockMailClient.Object);
// verify result
mockMailClient.Verify(client => client.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.AtLeastOnce);
System.Console.WriteLine($"sent result: {result}");
}
}
Demo 2 - make unit testing for entity framework based logic
-
Create entity framework basic entities and dbcontext
public class BloggingContext : DbContext { public virtual DbSet<Blog> Blogs { get; set; } // property must be virtual so that moq can create proxy public virtual DbSet<Post> Posts { get; set; } // property must be virtual so that moq can create proxy } public class Blog { public int BlogId { get; set; } public string Name { get; set; } public string Url { get; set; } public virtual List<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public virtual Blog Blog { get; set; } }
Please note that the DbSet properties on the context are marked as virtual. This will allow the mocking framework to derive from our context and overriding these properties with a mocked implementation.
-
Create the service to be tested
public class BlogService { private BloggingContext _context; public BlogService(BloggingContext context) { _context = context; } public Blog AddBlog(string name, string url) { var blog = _context.Blogs.Add(new Blog { Name = name, Url = url }); _context.SaveChanges(); return blog; } public List<Blog> GetAllBlogs() { var query = from b in _context.Blogs orderby b.Name select b; return query.ToList(); } public async Task<List<Blog>> GetAllBlogsAsync() { var query = from b in _context.Blogs orderby b.Name select b; return await query.ToListAsync(); } }
-
Create mocks and test the saving logic
// create service instance[TestClass] public class NonQueryTests { [TestMethod] public void CreateBlog_saves_a_blog_via_context() { // set up mocks var mockSet = new Mock<DbSet<Blog>>(); var mockContext = new Mock<BloggingContext>(); mockContext.Setup(m => m.Blogs).Returns(mockSet.Object);
// verifyvar service = new BlogService(mockContext.Object); service.AddBlog("ADO.NET Blog", "http://blogs.msdn.com/adonet");
mockSet.Verify(m => m.Add(It.IsAny<Blog>()), Times.Once()); mockContext.Verify(m => m.SaveChanges(), Times.Once()); } }
Here are steps:
- uses Moq to create a mock context
- creates a mock DbSet
and wires it up to be returned from the context’s Blogs property - create a new BlogService using mock context. Then create a new blog using the AddBlog() method.
- verifies that the service added a new Blog and called SaveChanges on the context.
-
Create in-memory data and test the query logic
[TestClass] public class QueryTests { [TestMethod] public void GetAllBlogs_orders_by_name() { var data = new List<Blog> { new Blog { Name = "BBB" }, new Blog { Name = "ZZZ" }, new Blog { Name = "AAA" }, }.AsQueryable(); var mockSet = new Mock<DbSet<Blog>>(); mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider); mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(0 => data.GetEnumerator()); var mockContext = new Mock<BloggingContext>(); mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); var service = new BlogService(mockContext.Object); var blogs = service.GetAllBlogs(); Assert.AreEqual(3, blogs.Count); Assert.AreEqual("AAA", blogs[0].Name); Assert.AreEqual("BBB", blogs[1].Name); Assert.AreEqual("ZZZ", blogs[2].Name); } }
Here are steps:
- create some in-memory data – List
. - create a mock context and mock DBSet
then wire up the IQueryable implementation for the DbSet – they’re just delegating to the LINQ to Objects provider that works with List .
- create some in-memory data – List
-
Test the async query logic
In order to use the async methods we need to create an in-memory DbAsyncQueryProvider to process the async query.
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider { private readonly IQueryProvider _inner; internal TestDbAsyncQueryProvider(IQueryProvider inner) { _inner = inner; } public IQueryable CreateQuery(Expression expression) { return new TestDbAsyncEnumerable<TEntity>(expression); } public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new TestDbAsyncEnumerable<TElement>(expression); } public object Execute(Expression expression) { return _inner.Execute(expression); } public TResult Execute<TResult>(Expression expression) { return _inner.Execute<TResult>(expression); } public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute(expression)); } public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) { return Task.FromResult(Execute<TResult>(expression)); } } internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T> { public TestDbAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { } public TestDbAsyncEnumerable(Expression expression) : base(expression) { } public IDbAsyncEnumerator<T> GetAsyncEnumerator() { return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator()); } IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() { return GetAsyncEnumerator(); } IQueryProvider IQueryable.Provider { get { return new TestDbAsyncQueryProvider<T>(this); } } } internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> { private readonly IEnumerator<T> _inner; public TestDbAsyncEnumerator(IEnumerator<T> inner) { _inner = inner; } public void Dispose() { _inner.Dispose(); } public Task<bool> MoveNextAsync(CancellationToken cancellationToken) { return Task.FromResult(_inner.MoveNext()); } public T Current { get { return _inner.Current; } } object IDbAsyncEnumerator.Current { get { return Current; } } }
Then, use our Moq DbSet to test GetAllBlogsAsync() method
[TestClass] public class AsyncQueryTests { [TestMethod] public async Task GetAllBlogsAsync_orders_by_name() { var data = new List<Blog> { new Blog { Name = "BBB" }, new Blog { Name = "ZZZ" }, new Blog { Name = "AAA" }, }.AsQueryable(); var mockSet = new Mock<DbSet<Blog>>(); mockSet.As<IDbAsyncEnumerable<Blog>>() .Setup(m => m.GetAsyncEnumerator()) .Returns(new TestDbAsyncEnumerator<Blog>(data.GetEnumerator())); mockSet.As<IQueryable<Blog>>() .Setup(m => m.Provider) .Returns(new TestDbAsyncQueryProvider<Blog>(data.Provider)); mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); var mockContext = new Mock<BloggingContext>(); mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); var service = new BlogService(mockContext.Object); var blogs = await service.GetAllBlogsAsync(); Assert.AreEqual(3, blogs.Count); Assert.AreEqual("AAA", blogs[0].Name); Assert.AreEqual("BBB", blogs[1].Name); Assert.AreEqual("ZZZ", blogs[2].Name); } }
FAQ
-
loose mock vs. strict mock
By default, Moq use loose mock strategy for every mock object and it saves lines of code. If we specify a mock object as strict mode, each method of the mock object must has a corresponding Setup() method
var mockObj = new Mock<IMailClient>(MockBehavior.Strict);
Generally, we prefer loose mock as strict mock might break whenever we modidy the unit testing.
-
what can be mocked?
Moq and other similar mocking frameworks can only mock interfaces, abstract methods/properties (on abstract classes) or virtual methods/properties on concrete classes.
This is because Moq generates a proxy that will implement the interface or create a derived class that overrides those overrideable methods in order to intercept calls.
References
Entity Framework Testing with a Mocking Framework