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

使用Moq框架单元测试时Mock接口返回null而非预期对象问题排查

Moq Mock Returns Null Instead of Preset IpApplication Object

Problem

When unit testing my ApplicationsManager BLL class, I'm using Moq to mock the IApplicationsManagerDal database interface. However, when calling AddIpApplication, the mocked method always returns null instead of the preset IpApplication object I defined.

Relevant Code

BLL Layer (ApplicationsManager):

public ApplicationsManager(IApplicationsManagerDal applicationsManagerDal) 
{ 
    _applicationsManagerDal = applicationsManagerDal ?? throw new Exception("applicationsManagerDAL is null"); 
}

public IpApplication AddIpApplication(IpApplication ipApplication) 
{ 
    try 
    { 
        if (Validator.ExecuteValidation(ipApplication, new AddIpApplicationValidationRules()).ValidationFailures != null) 
            return ipApplication; 
            
        ipApplication = _applicationsManagerDal.AddIpApplication(ipApplication); 
        ipApplication.Status = CommonStatus.OperationOk; 
    } 
    catch (BaseException e) 
    { 
        ipApplication.Status = e.Status; 
    } 
    catch (Exception e) 
    { 
        ipApplication.Status = ExceptionHandler.SetStatusForException(); 
        NLogger.LogException("IpApplication Name:" + ipApplication.Name, e); 
    } 
    return ipApplication; 
}

Moq Setup:

var _mock = new Mock<IApplicationsManagerDal>();
_mock.Setup(a => a.AddIpApplication(new IpApplication { 
    Code = new Guid("00000000-B935-4E12-9037-000000000000"), 
    Name = "APPLICATION_ZERO" 
}))
.Returns(() => new IpApplication { 
    Code = new Guid("9631E691-B935-4E12-9037-2E874DB15B0D"), 
    Description = "Test application", 
    Enabled = true, 
    Name = "APPLICATION_ZERO" 
});

Unit Test:

[TestMethod(), TestCategory("Unit")]
public void AddIpApplication_ValidRequest_IpApplicationSuccessfullyAdded() 
{ 
    // Arrange 
    _ipApplication = new IpApplication { 
        Code = new Guid("9631E691-B935-4E12-9037-2E874DB15B0D"), 
        Name = "APPLICATION_ZERO" 
    }; 

    // Act 
    var _ipApplication = _applicationManager.AddIpApplication(_ipApplication); 

    Assert.IsTrue(_ipApplication.Code != Guid.Empty); 
}

Root Cause

Moq uses reference equality by default when matching method arguments. In your setup, you're creating a brand new IpApplication instance as the parameter matcher. But in your test, you're passing a completely different IpApplication instance (even though some property values match). Since these are two separate objects in memory, Moq doesn't recognize the call as a match for your setup, so it returns the default value for the return type (null for reference types).

Solution

You need to adjust your Moq setup to match the parameter by its property values instead of relying on reference equality. Here are a few ways to do this:

1. Use It.Is<T>() to Match Specific Property Values

This is the most precise approach if you need to verify specific property values are being passed:

_mock.Setup(a => a.AddIpApplication(It.Is<IpApplication>(app => 
    app.Name == "APPLICATION_ZERO" &&
    app.Code == new Guid("9631E691-B935-4E12-9037-2E874DB15B0D"))))
.Returns(() => new IpApplication { 
    Code = new Guid("9631E691-B935-4E12-9037-2E874DB15B0D"), 
    Description = "Test application", 
    Enabled = true, 
    Name = "APPLICATION_ZERO" 
});

2. Implement Value Equality for IpApplication

If you control the IpApplication class, you can override Equals and GetHashCode to compare property values instead of references. Once you do that, Moq will use this custom equality check for parameter matching:

public class IpApplication
{
    // Your existing properties...

    public override bool Equals(object obj)
    {
        if (obj is not IpApplication other) return false;
        return Code == other.Code && Name == other.Name;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Code, Name);
    }
}

With this in place, your original setup would work as expected.

3. Use It.IsAny<IpApplication>() (If Parameter Specificity Doesn't Matter)

If you don't need to validate the exact parameter being passed and just want to return your preset object for any call to AddIpApplication, use this:

_mock.Setup(a => a.AddIpApplication(It.IsAny<IpApplication>()))
.Returns(() => new IpApplication { 
    Code = new Guid("9631E691-B935-4E12-9037-2E874DB15B0D"), 
    Description = "Test application", 
    Enabled = true, 
    Name = "APPLICATION_ZERO" 
});

Additional Fix for Your Test Code

You have a variable name collision in your test:

// Act 
var _ipApplication = _applicationManager.AddIpApplication(_ipApplication); 

You're reassigning the same _ipApplication variable. Change this to use a distinct variable name to avoid confusion and potential bugs:

// Act 
var result = _applicationManager.AddIpApplication(_ipApplication); 

// Assert
Assert.IsTrue(result.Code != Guid.Empty);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:56:10