我正在测试以下控制器:
public class MyController : ApiController
{
private readonly IUserManager _users;
private int num = 0;
public MyController(IUserManager users)
{
_users = users;
}
public User GetByCredentials(string username, string pass)
{
var account = new Account(username, pass);
User u = _users.GetByCredentials(num, account);
return u;
}
我正在考虑模拟 IUserManager.GetByCredentials 方法,因为我只想看到 MyController.GetByCredentials 方法按预期工作。问题是 User 类无法直接实例化,因此我无法模拟User对象,因为构造函数是私有的:
public class User
{
// private attributes here
protected User()
{
// do a lot of stuff here, call other objects to initialize attributes
}
private User(int num, string id, string userid, Account account)
: this()
{
// do other stuff here with the params
}
public static User CreateUser(int num, string id, string userid, Account account)
{
return new User(num, id, userid, account);
}
// and so on
}
我正在使用Moq框架,但我对不同的解决方案持开放态度。在这种情况下,我宁愿避免创建测试数据,因为它依赖于数据库的初始化,服务器等等 - 然后它就不再是单元测试了。你有过像这样的问题吗?你如何解决?谢谢。
答案 0 :(得分:3)
您无需模拟User
- 您可以使用真正的User
类。你只需要模拟(或伪造)IUserManager
。您的模拟/假冒IUserManager
可以使用User.CreateUser
创建要返回给您的控制器的User
个对象。
除非User
类本身“知道”数据库,否则应该没问题。抵制模仿一切的诱惑 - 你只需要摆脱那些难以编写测试的依赖。
现在你已经写过你的User
私有构造函数“做了很多事情” - 如果这太过深了,你应该重新设计User
,这样就更简单了...你的用户经理应该负责那里正在进行的一些工作。
虽然将世界完全分解为服务(如用户管理器)和愚蠢的数据对象(如用户)是过于简单化的,但如果设计合理自然将其自身分割,则会使事情变得更容易尊重。
答案 1 :(得分:3)
为了测试您的MyController
,您将嘲笑IUserManager
而不是User
,这样您就可以做到这样的事情:
var mockUserManager = new Mock<IUserManager>();
// Configure the User to be returned by calling GetByCredentials
mockUserManager
.Setup(x => x.GetByCredentials(It.IsAny<int>(), It.IsAny<Account>())
.Returns(User.CreateUser(1, "foo", "username", new Account());
var controller = new MyController(mockUserManager.Object);
var user = controller.GetByCredentials("username", "password");
Assert.NotNull(user);
mockUserManager.Verify(x => x.GetByCredentials(It.IsAny<int>(), It.IsAny<Account>(), Times.Once());
模拟/伪造对象的要点是避免数据库/ Web服务调用。您的Account
和User
类应该是poco类 - 例如只包含对自身起作用的方法和属性,没有数据库或Web服务调用等,因此它们实际上不需要模拟。