从存储库中模拟 Returns Null

Mocking From Repository Returns Null

我是 Mocking 的新手,不确定我是否做对了。我正在尝试使用函数 WriteNameToDatabase,它使用 GetName 方法,所以我的 类 设置如下:

我的员工界面:

public interface IEmployee
{
    Task<string> GetName();
   string WriteNameToDataBase();

}

我的员工Class

 public class Employee : IEmployee
    {
        public Task<string> GetName()
        {
            throw new NotImplementedException();
        }

        public string WriteNameToDataBase()
        {
            var employeeName = GetName();

            return $"Wrote {employeeName.Result} to database";

        }

我的单元测试Class

 [Test]

    public void WriteToDBShouldWrite()
    {
        var mockObject = new Mock<IEmployee>();
        mockObject.Setup(x => x.GetName()).ReturnsAsync("Frank");

        string result = mockObject.Object.WriteNameToDataBase();

        Assert.AreEqual("Wrote Frank to database", result);

    }

这总是 returns 我 Was Expecting 'Wrote Frank to database 但实际上是 null` 或 Assert 给我的那种效果。我做错了什么?

谢谢

编辑:更正为使用 ReturnsAsync("Frank");

几个问题,一个是你没有等待你的异步调用:

    public async Task<string> WriteNameToDataBase()
    {
        var employeeName = await GetName();

        return $"Wrote {employeeName} to database";

    }

两个你的mocking结果不对,moq支持async:

mockObject.Setup(x => x.GetName()).ReturnsAsync("Frank");

但是这里的大问题是你模拟了整个对象。这不是在测试任何东西。您可能想要更像:

public class Employee : IEmployee
{
    private readonly INameRepo _repo;
    public Employee(INameRepo repo)
    {
         //this is dependancy injection
         _repo = repo;
    }

    public async Task<string> WriteNameToDataBase()
    {
        var employeeName = await _repo.GetName();

        return $"Wrote {employeeName.Result} to database";

    }
 }

你的 repo 现在是这个接口所代表的一个独立的东西

public interface INameRepo
{
      Task<string> GetName();
}

然后你的测试看起来像:

[Test]
//note this is also async now
public async Task WriteToDBShouldWrite()
{
    //mock the dependencies
    var mockObject = new Mock<INameRepo>();
    mockObject.Setup(x => x.GetName()).ReturnsAsync("Frank");

    //test the actual object
    Employee emp = new Employee(mockObject.Object);
    string result = await emp.WriteToDBShouldWrite();

    Assert.AreEqual("Wrote Frank to database", result);

}

如果您只想验证 GetName 是否被调用,您应该使用 Verify 而不是返回任意值:

[Test]
//note this is also async now
public async Task WriteToDBShouldWrite()
{
    var mockObject = new Mock<INameRepo>();
    mockObject.Setup(x => x.GetName()).ReturnsAsync("Frank");

    string result = mockObject.Object.WriteNameToDataBase();

    Employee emp = new Employee(mockObject.Object);
    await emp.WriteToDBShouldWrite();

    //ensure GetName is called once and once only
    mockObject.Verify(v => v.GetName(), TimesOnce);

}

请注意,您是基于接口 IEmployee 而不是 Class Employee 创建 Mock 对象,因此您不是在测试 Employee class,因此您不能指望 Employee class 中的任何代码是 运行.

您需要考虑您的测试实际上要做什么,以及什么构成了您项目中的 "unit"。

您可以创建一个包含 GetName 方法的新接口 INameGetter(或名称对您的项目有意义的名称)。然后,您可以将这个新接口作为您的员工 class 的依赖项,并在您的测试中对其进行模拟。类似于:

public interface IEmployee
{
   Task<string> WriteNameToDataBase();
}

public interface INameGetter
{
   Task<string> GetName();
}

public class Employee : IEmployee
{
     private INameGetter nameGetter;

     public Employee(INameGetter n)
     {
         nameGetter = n;
     }

    public Task<string> WriteNameToDataBase()
    {
        var employeeName = await nameGetter.GetName();

        return $"Wrote {employeeName} to database";
    }
}

还有你的测试方法

[Test]
public void WriteToDBShouldWrite()
{
    var mockObject = new Mock<INameGetter>();
    mockObject.Setup(x =>  x.GetName()).Returns(Task.FromResult("Frank"));

    string result =  new Employee(mockObject.Object).WriteNameToDataBase();

    Assert.AreEqual("Wrote Frank to database", result);

}

N.B 您还需要研究 async/await 以了解使用返回 Task 的方法。编辑:我现在在这里修复了它,但基本上你不想使用 Task.Result.