开始之前:
之前已经在不同程度Like here, where they talk about it, however we do NOT implement the repository pattern 提出了这类问题,但我觉得它没有以简洁的方式回答,所以我再次提出这个问题。 欢迎参考或一般指南,所以提前谢谢。
这是我到目前为止所拥有的。 控制器:
public class StartTripController : Controller
{
private readonly DbContext _dbContext;
public StartTripController(DbContext DbContext) => _DbContext = DbContext;
[Route("connect")]
[HttpGet]
public async Task<IActionResult> StartTrip(MessageDto messageDto, StartTripDto startTripDto)
{
if (ModelState.ErrorCount > 0)
return StatusCode(400);
var userToCheck = await _DbContext.User
.Select(i => new UserDto { UserId = i.Id, PhoneId = i.PhoneId, AppInfoDto = new AppInfoDto { IsAppInDebug = false } })
.SingleOrDefaultAsync(u => u.PhoneId == startTripDto.UserDto.PhoneId); //checks if User is in DB, returns Null if not
if (userToCheck == null) //user does not exist
{
//Make new User entity and save the changes to DB async
UserDto newUserToReturn = new UserDto { UserId = user.Id, AppInfoDto = new AppInfoDto { IsAppInDebug = user.DebugMode } };
return GenerateResponseWithStatus200(messageDto, newUserToReturn);
}
//user exists
return GenerateResponseWithStatus200(messageDto, userToCheck);
}
我的测试看起来像这样:
public class StartTripControllerTest : ControllerTest<StartTripController>
{
private DbContext _mockDbContext;
protected override StartTripController GetController()
{
var mockDbContext = new Mock<DbContext>();
var userData = new List<User>
{
new User{PhoneId = "Phone1", Id = 1, ReportProviderId = 1, UserPhone = null, DebugMode = true, IpAddress = "empty", DeviceUser = null, Credential = null},
new User{PhoneId = "Phone2", Id = 2, ReportProviderId = 2, UserPhone = null, DebugMode = true, IpAddress = "empty", DeviceUser = null, Credential = null}
};
var mockData = userData.AsQueryable().BuildMock(); //BuildMock is from https://github.com/romantitov/MockQueryable
mockDbContext.Setup(x => ???what do I write here??).Returns(mockData.Object);
return new StartTripController(mockDbContext.Object);
}
[Fact]
public async System.Threading.Tasks.Task StartTrip_ReturnUser_JsonAsync()
{
// Arrange
StartTripController startTripController = GetController();
MessageDto messageDto = new MessageDto ();
StartTripDto startTripDto = new StartTripDto();
//code omitted for readiblity
var result = await startTripController.StartTrip(messageDto, startTripDto);
}
}
我想出的事情:
我坚持的事情:
答案 0 :(得分:1)
所以我想分享一下我对这个问题的回答。 @Fabio建议的主要是我在原始问题中发布的第一个参考主题。
我将我的数据库交互移动到&#34; UserManager&#34;如果您愿意,可以提供服务现在我的控制器看起来像这样:
public class StartTripController : Controller
{
private readonly IUserManager _userManager;
public StartTripController( IUserManager userManager)
{
_userManager = userManager;
}
[Route("connect")]
[HttpGet]
public async Task<IActionResult> StartTrip(MessageDto messageDto, StartTripDto startTripDto)
{
messageDto.Message = Any.Pack(startTripDto);
if (ModelState.ErrorCount > 0)
return StatusCode(400);
var userToCheck = await _userManager.FindUser(startTripDto.UserDto);
if (userToCheck == null) //user does not exist
{
var newUser = await _userManager.AddUser(startTripDto.UserDto);
return GenerateResponseWithStatus200(messageDto, newUser);
}
//user exists
await _userManager.StartTripExistingUser(userToCheck);
return GenerateResponseWithStatus200(messageDto, userToCheck);
}
}
这自动改变了我的测试,因为我没有模拟数据库,这大大简化了我的问题。
我的测试看起来像这样: public class StartTripControllerTest:ControllerTest {
protected override StartTripController GetController()
{
var mockUserManager = new Mock<IUserManager>();
AppInfoDto appInfoDto = new AppInfoDto {IsAppInDebug = true};
UserDto userDto = new UserDto {UserId = 1818, PhoneId = "Phone1", AppInfoDto = appInfoDto};
mockUserManager.Setup(p => p.FindUser(It.IsAny<UserDto>())).Returns(Task.FromResult(userDto));
return new StartTripController(mockUserManager.Object);
}
[Fact]
[Trait("Unit", "Controller")]
public void StartTrip_ReturnUser_BadRequestAsync()
{
// Arrange
StartTripController startTripController = GetController();
MessageDto messageDto = new MessageDto { ApiVersion = "1.3" };
AppInfoDto appInfoDto = new AppInfoDto { IsAppInDebug = true };
UserDto userDto = new UserDto { PhoneId = "Phone1", AppInfoDto = appInfoDto };
StartTripDto startTripDto = new StartTripDto { UserDto = userDto };
startTripController.ModelState.AddModelError(ModelBinderError.MissingUserId.errorKey, ModelBinderError.MissingUserId.errorValue);
var result = startTripController.StartTrip(messageDto, startTripDto).Result as StatusCodeResult;
Assert.True(result.StatusCode == 400);
}
}
}
上面的例子显示了Controller初始化&#34; GetController&#34;方法,还显示了如何使用ModelState。
然而,这并没有解决潜在的问题,只是将其移动到系统的不同部分。当您必须测试UserManager时,您仍然遇到模拟数据库的问题。
为了测试系统的这一部分,您需要进行交互测试。 对于SQLServer,您可以使用InMemoryDatabase,但是因为我使用的是Postgresql,所以我需要使用TestDatabase。
总结一下我的UserManager测试看起来像这样:
public class UserManagerIntegrationTests
{
private readonly TestServer _server;
private readonly HttpClient _client;
public UserManagerIntegrationTests()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<StartupWithTestDatabase>());//Startup file contains the TestDatabase connection string
_client = _server.CreateClient();
}
// ...
}
P.S。我现在正在查看的是Fluent Assertions,它基本上用改变的方法替换了正常的Assert.True()。
PPS仅供将来参考,我在ASP.NET Core 2的单元测试中找到的最新教程是this one,它还展示了一个示例,您可以在其中与控制器和与之配合的服务进行集成测试你的数据库。