我正在使用xUnit来测试我的项目。我有一项测试,用于检查user
是否添加了list
users
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;
}
现在我知道代码是有效的,因为我先写了它,但是当我运行测试时它失败了!下面是我将用户添加到用户列表的代码:
public List<User> GetUsers()
{
return _jsonService.DeserializeObject<User>(_fileName).ToList();
}
GetUser方法:
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);
}
}
FileSystem类:
var serializeObject = _jsonService.SerializeObject(_fileName, userList);
当我运行测试时,AddUser
方法中的{{1}}每次都返回false。
我认为这样做是因为即使它与预期结果的数据相同,但在内存中它是对同一数据的不同引用。
任何人都可以帮助我,我希望它返回相同的引用数据。如果我不是很清楚,我可以详细说明。感谢
答案 0 :(得分:1)
你是绝对正确的,问题的根源在于_fakeUserList
和_fakeUpdatedUserList
引用两个完全不同的对象。您已将_jsonService.SerializeObject
配置为在true
引用时返回_fakeUpdatedUserList
- 但您实际上已将引用传递给(已修改的)_fakeUserList
}。
基本上,_fakeUpdatedUserList
完全没必要。您可以专注于_fakeUserList
,因为这是提供给SUT的对象(可能是通过DeserializeObject<User>
)。
例如:
[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
等)。此代码显示您在ToList()
返回的IEnumerable<User>
上调用DeserializeObject<User>
。这就是您的模拟对象没有按预期运行的原因:ToList()
返回的列表与_fakeUserList
完全不同。
此外,我在测试中没有看到_fakeUserJsonFile
被注入SUT的任何地方。因此,_fileName
中的AddUser
可能不是您所期望的。
要解决此问题,您需要修改设计(例如,不要调用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);