Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why can't I just write a "Fake" class manually instead of using Moq?

You totally can. In the early days, we all did. You'd create a FakePaymentGateway : IPaymentGateway class, add some boolean flags to track if a method was called, and manually set return values. But here is the problem: as soon as you need a slightly different behavior for a second test—like making the payment fail instead of succeed—you either have to add more complex logic to your fake or create FakePaymentGatewaySuccess and FakePaymentGatewayFailure.

It becomes a maintenance nightmare. Moq lets you create these "fakes" on the fly inside your test method. Instead of writing a whole new class, you just tell Moq, "For this specific test, when this method is called with these arguments, return this value." It keeps your test project clean and your intent clear.

How do I actually tell a mock what to return?

The core of Moq is the Setup method. Think of it as programming the behavior of your dependency for a specific scenario. Let's say we have an OrderService that depends on an IPaymentGateway. We don't want to hit a real API and spend real money during a unit test, right?


// Setup the mock
var paymentMock = new Mock<IPaymentGateway>();

// Tell it: "Whenever ProcessPayment is called with any amount, return true"
paymentMock.Setup(p => p.ProcessPayment(It.IsAny<decimal>()))
            .Returns(true);

// Inject the .Object (the actual generated instance) into your service
var service = new OrderService(paymentMock.Object);
var result = service.PlaceOrder(myOrder);

Assert.True(result.Success);

Notice It.IsAny<decimal>(). That's a Moq matcher. I'm telling Moq I don't care what the specific amount is for this test; I just want the payment to succeed regardless of the price.

How do I verify that a method was actually called?

Sometimes, a method doesn't return a value you can assert against. For example, if your OrderService calls an IEmailService to send a confirmation, the SendEmail method probably returns void. You can't check a return value, so you check the behavior.

This is where Verify comes in. It's essentially asking the mock, "Did this happen?"


var emailMock = new Mock<IEmailService>();
var service = new OrderService(paymentMock.Object, emailMock.Object);

service.PlaceOrder(myOrder);

// Verify that SendEmail was called exactly once with the customer's email
emailMock.Verify(e => e.SendEmail(myOrder.CustomerEmail, "Order Confirmed"), Times.Once);

I personally find Verify incredibly powerful for testing "side effects." If your code accidentally sends five emails to a customer because of a loop bug, Times.Once will catch that and fail the test immediately.

What happens if I forget to Setup a method?

By default, Moq uses "Loose" mocks. If you call a method that hasn't been set up, it won't throw an exception. Instead, it returns the default value for the return type—null for objects, false for booleans, and 0 for integers.

This is usually fine, but if you want your tests to fail the moment an unexpected call is made, you can set the MockBehavior.Strict option when creating the mock. I generally advise against Strict mocks unless you're dealing with a very fragile legacy system; they tend to make tests brittle because every single interaction must be explicitly defined, even the trivial ones.




📋 Practical Task

Exercise: Simulating a Payment Failure in the Order Processor

You are tasked with testing the error-handling logic of the OrderService. Your goal is to ensure that if the payment gateway fails, the order is marked as "Failed" and no confirmation email is sent to the customer.

Requirements:

  • Create a mock for IPaymentGateway and setup ProcessPayment to return false.
  • Create a mock for IEmailService.
  • Call OrderService.PlaceOrder(order).
  • Assert that the order status is OrderStatus.Failed.
  • Use Verify to ensure that IEmailService.SendEmail was never called.

Starter Code Context:
public interface IPaymentGateway { bool ProcessPayment(decimal amount); }
public interface IEmailService { void SendEmail(string email, string message); }
public class Order { public string CustomerEmail { get; set; } public OrderStatus Status { get; set; } }

Rating
0 0

There are no comments for now.

to be the first to leave a comment.