【问题标题】:Mocking only two methods in class for unit testing Moq and XUnit在类中仅模拟两种用于单元测试 Moq 和 XUnit 的方法
【发布时间】:2021-12-04 01:25:39
【问题描述】:

希望你做得很好。

我们的服务调用方法GetUserAccountNo() 依次调用该服务中的其他两个(GetUserCreateUser)。 我们正在使用 Moq 和 XUnit 进行测试 谁能告诉我如何只模拟GetUserCreateUser 并为GetUserAccountNo 编写单元测试?

我可以看到有人说将方法设为virtual。 在这种情况下,所有方法都在同一个类中,来自同一个接口和public

请找代码sn-p

public int GetUserAccountNo (string id)
{
    if (GetUser(id)!=null )
    {
        return GetUser(id).AccountNo;
    }
    else
    {
        return CreateUser(id).AccountNo;
    }
}


public User GetUser(string id)
{
    //Get from  userAPi
    return new User();
}

public User CreateUser(string id)
{
    //Create from userAPi using  http request
    return new User();
}

【问题讨论】:

  • 欢迎来到 StackOverflow。你都尝试了些什么?你在哪里卡住了?

标签: c# unit-testing moq xunit


【解决方案1】:

这感觉就像XY problem。通常,您不会模拟 SUT(被测系统)的某些部分。通常,您会将用户 API 注入您的服务并对其进行模拟。不过还是可以的。

将方法标记为虚拟并像往常一样模拟它们;工作 LINQPad 示例:

void Main()
{
    var fixture = new Fixture();
    var existingUser = fixture.Create<User>();
    var newUser = fixture.Create<User>();

    var fooMock = new Mock<FooService>();
    fooMock.Setup(x => x.GetUser(It.Is<string>(y => y.Equals(existingUser.Id)))).Returns(() => existingUser);
    fooMock.Setup(x => x.CreateUser(It.IsAny<string>())).Returns(() => newUser);
    var foo = fooMock.Object;

    foo.GetUserAccountNo(existingUser.Id).Should().Be(existingUser.AccountNo);
    foo.GetUserAccountNo(newUser.Id).Should().Be(newUser.AccountNo);
}

// You can define other methods, fields, classes and namespaces here
public class FooService
{
    public FooService() { }

    public int GetUserAccountNo(string id)
    {
        if (GetUser(id) != null)
        {
            return GetUser(id).AccountNo;
        }
        else
        {
            return CreateUser(id).AccountNo;
        }
    }

    public virtual User GetUser(string id)
    {
        //Get from User API
        throw new NotImplementedException();
    }

    public virtual User CreateUser(string id)
    {

        //Get from User API
        throw new NotImplementedException();
    }
}

public class User
{
    public string Id { get; set; }

    public int AccountNo { get; set; }
}

也许您的情况有充分的理由,但这通常不是一个好方法;您已修改 SUT 以进行测试;你没有利用依赖注入的一大好处——用其他东西代替依赖的能力;而且,当你在模拟一个实现时,你还需要满足构造函数的要求——为简洁起见,我使用了一个无参数的构造函数。

如果我们可以注入和模拟用户 API,那么以下将是正常的方法:

void Main()
{
    var fixture = new Fixture();
    var existingUser = fixture.Create<User>();
    var newUser = fixture.Create<User>();

    var userApiMock = new Mock<IUserApi>();
    userApiMock.Setup(x => x.GetUser(It.Is<string>(y => y.Equals(existingUser.Id)))).Returns(() => existingUser);
    userApiMock.Setup(x => x.CreateUser(It.IsAny<string>())).Returns(() => newUser);
    var userApi = userApiMock.Object;

    var foo = new FooService(userApi);

    foo.GetUserAccountNo(existingUser.Id).Should().Be(existingUser.AccountNo);
    foo.GetUserAccountNo(newUser.Id).Should().Be(newUser.AccountNo);
}

// You can define other methods, fields, classes and namespaces here
public interface IUserApi
{
    User GetUser(string id);

    User CreateUser(string id);
}

public class FooService
{
    IUserApi _userApi;

    public FooService(IUserApi userApi)
    {
        _userApi = userApi;
    }

    public int GetUserAccountNo(string id)
    {
        if (GetUser(id) != null)
        {
            return GetUser(id).AccountNo;
        }
        else
        {
            return CreateUser(id).AccountNo;
        }
    }

    public User GetUser(string id)
    {
        return _userApi.GetUser(id);
    }

    public User CreateUser(string id)
    {
        return _userApi.CreateUser(id);
    }
}

public class User
{
    public string Id { get; set; }

    public int AccountNo { get; set; }
}

【讨论】:

    猜你喜欢
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-18
    • 2021-04-12
    • 2020-02-18
    • 2019-09-27
    • 1970-01-01
    • 2016-01-04
    相关资源
    最近更新 更多