我正在尝试研究如何将单元测试添加到我的项目中。我认为最好先从一个空白项目开始,然后从一开始就将其解决,而不是将其添加到我的主项目中。一旦我理解了这个过程,我就可以开始重构我的项目以添加测试。
所以我创建了一个Web应用程序并为其添加了默认用户身份。
这给了我一个像这样的启动
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
然后我创建了一个简单的控制器并在构造函数中传递了usermanager。
[Route("api/[controller]")]
[ApiController]
public class SweetController : ControllerBase
{
private readonly UserManager<IdentityUser> _userManager;
public SweetController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task<int> NumberOfGummyBearsForUser(string userId)
{
var user = await _userManager.FindByIdAsync(userId);
var userHasASweetTooth = await _userManager.IsInRoleAsync(user, "SweetTooth");
if (userHasASweetTooth)
{
return 100;
}
else
{
return 1;
}
}
}
我一直试图做的第一件事是模拟这个用户管理器,但它无法正常工作。
public void Test1()
{
// Arrange
var mockUser = new Mock<UserManager<IdentityUser>>();
var userManager = new UserManager(mockRepo.Object); <-- error here see image below
var controller = new SweetController(userManager.Object);
// Act
var result = await controller.NumberOfGummyBearsForUser("123");
// Assert
Assert.Equal(100, result);
}
错误看起来像这样
我想我需要传递更多来创建这个usermanager对象,但我不确定我发现的所有教程都使用ApplicationUser而不是IdentityUser,所以我不知道如何模仿这个对象。
你这样做
// Arrange
var mockUser = new Mock<UserManager<IdentityUser>>();
var controller = new SweetController(mockUser.Object);
你不需要
var userManager = new UserManager(mockRepo.Object); <-- error here see image below
一点都不mockUser
已经是嘲笑的UserManager<T>
,你通过mock.Object
放置一个模拟的实例。
当您模拟一个对象时,您不必使用它的所有依赖项(即集成测试)来实例化它,这就是模拟的关键(同时使方法返回所需的值并进行行为测试以确保您测试的代码已经使用模拟对象的特定参数调用了特定方法。
当然,上面的代码本身不起作用,因为你没有为FindByIdAsync
和IsInRoleAsync
设置任何测试条件/回报。你必须设置这些
mockUser.Setup( userManager => userManager.FindByIdAsync(It.IsAny<string>()))
.ReturnsAsync(new IdentityUser { ... });
mockUser.Setup( userManager => userManager.IsInRoleAsync(It.IsAny<IdentityUser>(), "SweetTooth"))
.ReturnsAsync(true);
然后,只要调用mock,它就会返回预定义的用户和预定义的结果。
在Tseng的帮助下,我得到了这个工作。这是一个完全有效的版本
private readonly UserManager<IdentityUser> _userManager;
public SweetController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task<IdentityUser> GetUser(string userId)
{
var user = await _userManager.FindByIdAsync(userId);
return user;
}
[Fact]
public async Task Test1()
{
// Arrange
var store = new Mock<IUserStore<IdentityUser>>();
store.Setup(x => x.FindByIdAsync("123", CancellationToken.None))
.ReturnsAsync(new IdentityUser()
{
UserName = "[email protected]",
Id = "123"
});
var mgr = new UserManager<IdentityUser>(store.Object, null, null, null, null, null, null, null, null);
var controller = new SweetController(mgr);
// Act
var result = await controller.GetUser("123");
// Assert
Assert.NotNull(result);
Assert.Equal("123", result.Id);
}
我删除了角色检查,以使其尽可能基本工作。