【问题标题】:xUnit Test Add Model to List FailingxUnit 测试将模型添加到列表失败
【发布时间】:2014-07-16 11:11:24
【问题描述】:

我正在使用 xUnit 来测试我的项目。我有一个测试来检查 user 是否已添加到 users 的 list 中,如下所示:

private readonly IJsonService _jsonService;
private readonly IUserService _userService;

[Fact]
    public void Add_User_To_User_List()
    {
        //Given
        _userService = new UserService(_jsonService, _guidService);
        _jsonService = Substitute.For<IJsonService>();

        var _fakeUserJsonFile = "Users.json";
        var _fakeNewUser = new User()
        {
            ID = new Guid(),
            FirstName = "Denis",
            LastName = "Menis"
        };

        var _fakeUserList = new List<User>
        {
            new User()
                {
                    ID = new Guid(),
                    FirstName = "Paddy",
                    LastName = "Halle"
                },
            new User()
                {
                    ID = new Guid(),
                    FirstName = "Job",
                    LastName = "Blogs"
                }
        };

        var _fakeUpdatedUserList = new List<User>
        {
            new User()
                {
                    ID = new Guid(),
                    FirstName = "Paddy",
                    LastName = "Halle"
                },
            new User()
                {
                    ID = new Guid(),
                    FirstName = "Job",
                    LastName = "Blogs"
                },
            new User()
                {
                    ID = new Guid(),
                    FirstName = "Denis",
                    LastName = "Menis"
                }
        };

        _jsonService.DeserializeObject<User>(_fakeUserJsonFile).Returns(_fakeUserList);
        _jsonService.SerializeObject(_fakeUserJsonFile, _fakeUpdatedUserList).Returns(true);

        //When
        var result = _userService.AddUser(_fakeNewUser);

        //Then
        Assert.Contains(_fakeNewUser, _fakeUpdatedUserList);
    }

现在我知道代码有效,因为我先编写了它,但是当我运行测试时它失败了!下面是我将用户添加到用户列表的代码:

public bool AddUser(User user)
    {
        var userList = GetUsers();

        user.ID = _guidService.NewGuid();

        userList.Add(user);

        var serializeObject = _jsonService.SerializeObject(_fileName, userList);

        return serializeObject;
    }

GetUser 方法:

public List<User> GetUsers()
    {
        return _jsonService.DeserializeObject<User>(_fileName).ToList();
    }

反序列化方法:

private readonly IFileSystem _file;
    private readonly HttpContextBase _httpContext;
    private readonly ILogger _logger;

    public JsonService(IFileSystem file, HttpContextBase httpContext, ILogger logger)
    {
        _file = file;
        _httpContext = httpContext;
        _logger = logger;
    }

    public IEnumerable<T> DeserializeObject<T>(string fileName)
    {
        try
        {
            var relativeFileName = _httpContext.Server.MapPath(fileName);
            var readFile = _file.ReadAllText(relativeFileName);
            var list = JsonConvert.DeserializeObject<List<T>>(readFile);
            return list;
        }
        catch (Exception ex)
        {
            _logger.LogException(ex);
            return null;
        }
    }

文件系统类:

public class FileSystem : IFileSystem
{
    public void WriteAllText(string path, string contents)
    {
        File.WriteAllText(path, contents);
    }

    public string ReadAllText(string path)
    {
        return File.ReadAllText(path);
    }
}

当我运行测试时,AddUser 方法中的var serializeObject = _jsonService.SerializeObject(_fileName, userList); 每次都返回 false。

我认为这样做是因为即使它是与预期结果相同的数据,但在内存中它是对相同数据的不同引用。

谁能帮我解决这个问题,我希望它返回相同的引用数据。如果我不是很清楚,我可以详细说明。谢谢

【问题讨论】:

  • 你能显示一些设置代码吗?特别是,将_fileName 链接到_fakeUserJsonFile 和userList 链接到_fakeUpdatedUserList 的代码?
  • @Lilshieste 我已更新代码以显示数据
  • 谢谢!还有一个问题:您使用的是什么模拟框架? N替代? (即_jsonService的类型是什么?)
  • 我正在使用 NSubstitute 是的。我会更新_jsonService代码,它只是一个读写通用json文件的类
  • 你不能同时使用 _fakeUserList 和 _fakeUpdatedUserList 吗?

标签: c# asp.net unit-testing xunit nancy


【解决方案1】:

你说得对,问题的根源在于_fakeUserList 和_fakeUpdatedUserList 引用了两个完全不同的对象。您已将 _jsonService.SerializeObject 配置为在传递对 _fakeUpdatedUserList 的引用时返回 true - 但实际上您传递的是对(修改后的)_fakeUserList 的引用。

基本上,_fakeUpdatedUserList 是完全没有必要的。您可以关注 _fakeUserList,因为这是提供给 SUT 的对象(大概是通过 DeserializeObject&lt;User&gt;)。

例如:

[Fact]
public void Add_User_To_User_List()
{
    //Given
    _userService = new UserService(_jsonService, _guidService);
    _jsonService = Substitute.For<IJsonService>();

    var _fakeUserJsonFile = "Users.json";
    var _fakeNewUser = new User()
    {
        ID = new Guid(),
        FirstName = "Denis",
        LastName = "Menis"
    };

    var _fakeUserList = new List<User>
    {
        new User()
            {
                ID = new Guid(),
                FirstName = "Paddy",
                LastName = "Halle"
            },
        new User()
            {
                ID = new Guid(),
                FirstName = "Job",
                LastName = "Blogs"
            }
    };

    _jsonService.DeserializeObject<User>(_fakeUserJsonFile).Returns(_fakeUserList);
    _jsonService.SerializeObject(_fakeUserJsonFile, _fakeUserList).Returns(true); // Match the original _fakeUserList, since that is what gets passed in by the implementation

    //When
    var result = _userService.AddUser(_fakeNewUser);

    //Then
    Assert.Contains(_fakeNewUser, _fakeUserList); // Verify that the provided _fakeUserList has been modified
}

附带说明:您实际上可以从该测试中删除很多细节,因为它们与正在测试的功能无关。例如,_fakeUserList 最初可以为空 - 它不必包含任何虚拟值。您可以使用_fakeNewUser 的默认值(即,不指定FirstName 等),因为在此测试中根本没有引用它们。

编辑:感谢您发布附加代码(用于GetUsers 等)。此代码显示您正在对DeserializeObject&lt;User&gt; 返回的IEnumerable&lt;User&gt; 调用ToList()。这就是为什么您的模拟对象没有按预期运行的原因:ToList() 返回的列表与_fakeUserList 完全不同。

此外,我在您的测试中没有看到将_fakeUserJsonFile 注入SUT 的任何地方。所以AddUser 中的_fileName 也可能不是您所期望的。

要解决此问题,您要么需要修改设计(例如,不调用 ToList),要么修改测试中的预期行为。虽然您可能想考虑前者的可能性,但后者可能更容易。

例如:

    // Match any filename (unless you have a way of getting _fakeUserJsonFile into the SUT)
    // Match any list, as long as it contains the new user
    _jsonService.SerializeObject(Arg.Any<string>(), Arg.Is<List<User>>(list => list.Contains(_fakeNewUser))).Returns(true);

    //When
    var result = _userService.AddUser(_fakeNewUser);

    //Then
    Assert.IsTrue(result); // Only returns true if the mock object is invoked as expected

    // There is no way to verify the following assertion, because the test has no way of accessing the "updated" list
    //Assert.Contains(_fakeNewUser, _fakeUserList);

【讨论】:

  • 您好感谢您的回复,即使我只使用上面的代码,它仍然无法序列化用户,这就是为什么我认为它得到相同的变量(IE _fakeUserJsonFile & _fakeUserList)如果有意义的话,内存中的不同引用
  • 是的,您需要确保_fileName 引用与_fakeUserJsonFile 相同的对象。您还需要确保 GetUsers 返回由 DeserializeObject&lt;User&gt; 返回的列表(即,不是它的副本)。如果您发布GetUsers 的代码(以及设置_fileName 的代码),我们可以验证引用是否相同。
  • 我添加了getuser方法和反序列化方法
  • 问题出在DeserializeObject 方法中:当您调用ToList() 时,会使用IEnumerable 的内容创建一个新列表。 (这就是返回的列表与_fakeUserList不一样的原因。)
  • 有什么方法可以测试吗?还是我必须将方法更改为List 而不是IEnumerale?
猜你喜欢
  • 2014-07-12
  • 2022-06-30
  • 2020-03-23
  • 2011-05-09
  • 2019-11-21
  • 2019-09-04
  • 1970-01-01
  • 1970-01-01
  • 2019-03-12
相关资源
最近更新 更多