You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Moq.Mock或Rhino.Mocks模拟ObjOutlookApplication.Session.Stores编写单元测试

Got it, let's work through this unit testing challenge for your Outlook add-in. The main hurdle here is that Outlook’s native Application, Session, and Stores are COM objects—they aren’t built to be easily mocked directly. But with a bit of abstraction and a mocking framework like Moq, we can fully test your IsMyPstStoreIsAvailable method, including the foreach loop logic.

Step 1: Add Abstractions for Outlook Objects

First, we’ll create interfaces that mirror the parts of Outlook’s objects we need. This lets us mock them later without relying on the actual Outlook COM objects:

// Interface for a single Outlook Store
public interface IOutlookStore
{
    string StoreID { get; }
}

// Interface for Outlook's Session object
public interface IOutlookSession
{
    IEnumerable<IOutlookStore> Stores { get; }
}

// Interface for the main Outlook Application object
public interface IOutlookApplication
{
    IOutlookSession Session { get; }
}

Step 2: Refactor Your Service Class to Use Dependencies

Next, we’ll adjust your original class to depend on these interfaces instead of directly using the Outlook COM objects. This follows the Dependency Inversion Principle and makes testing straightforward:

public class OutlookStoreChecker
{
    private readonly IOutlookApplication _outlookApp;

    // Inject the abstracted Outlook application instead of using a concrete COM object
    public OutlookStoreChecker(IOutlookApplication outlookApp)
    {
        _outlookApp = outlookApp;
    }

    public bool IsMyPstStoreIsAvailable(string strStoreID)
    {
        foreach (IOutlookStore objStore in _outlookApp.Session.Stores)
        {
            if (objStore.StoreID == strStoreID)
                return true;
        }
        return false;
    }
}

Step 3: Write Unit Tests with Moq

Now we can use Moq to mock our abstractions, covering both scenarios: when the store exists (returns true) and when it doesn’t (returns false). I’ll use xUnit for the test framework, but this works with NUnit or MSTest too.

using Moq;
using Xunit;

public class OutlookStoreCheckerTests
{
    [Fact]
    public void IsMyPstStoreIsAvailable_StoreExists_ReturnsTrue()
    {
        // Arrange
        string targetStoreId = "MyTargetPSTStoreID";

        // Mock a store that matches our target ID
        var mockMatchingStore = new Mock<IOutlookStore>();
        mockMatchingStore.Setup(store => store.StoreID).Returns(targetStoreId);

        // Mock some other stores to simulate a real session
        var mockStore1 = new Mock<IOutlookStore>();
        mockStore1.Setup(store => store.StoreID).Returns("RandomStoreID1");

        var mockStore2 = new Mock<IOutlookStore>();
        mockStore2.Setup(store => store.StoreID).Returns("RandomStoreID2");

        // Mock the session and populate its Stores collection
        var mockSession = new Mock<IOutlookSession>();
        mockSession.Setup(session => session.Stores)
                   .Returns(new List<IOutlookStore> { mockStore1.Object, mockMatchingStore.Object, mockStore2.Object });

        // Mock the application to return our mocked session
        var mockApp = new Mock<IOutlookApplication>();
        mockApp.Setup(app => app.Session).Returns(mockSession.Object);

        // Create our service with the mocked dependencies
        var storeChecker = new OutlookStoreChecker(mockApp.Object);

        // Act
        bool result = storeChecker.IsMyPstStoreIsAvailable(targetStoreId);

        // Assert
        Assert.True(result);
    }

    [Fact]
    public void IsMyPstStoreIsAvailable_StoreDoesNotExist_ReturnsFalse()
    {
        // Arrange
        string targetStoreId = "NonExistentStoreID";

        // Mock stores that don't match our target ID
        var mockStore1 = new Mock<IOutlookStore>();
        mockStore1.Setup(store => store.StoreID).Returns("ExistingStoreID1");

        var mockStore2 = new Mock<IOutlookStore>();
        mockStore2.Setup(store => store.StoreID).Returns("ExistingStoreID2");

        var mockSession = new Mock<IOutlookSession>();
        mockSession.Setup(session => session.Stores)
                   .Returns(new List<IOutlookStore> { mockStore1.Object, mockStore2.Object });

        var mockApp = new Mock<IOutlookApplication>();
        mockApp.Setup(app => app.Session).Returns(mockSession.Object);

        var storeChecker = new OutlookStoreChecker(mockApp.Object);

        // Act
        bool result = storeChecker.IsMyPstStoreIsAvailable(targetStoreId);

        // Assert
        Assert.False(result);
    }
}

Why This Works

By abstracting the Outlook objects, we’ve decoupled our business logic from the concrete COM implementation. Now we can fully control the Stores collection in tests: we can add stores with matching IDs to test the return true path, and omit the target ID to test the full loop + return false path.

If you can’t do full refactoring for some reason, paid tools like TypeMock or JustMock can mock sealed COM objects directly—but the abstraction approach is cleaner, more maintainable, and uses free, open-source tools.

内容的提问来源于stack exchange,提问作者Pratik Ratanpara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:26:30