在ASP.NET Core Web应用程序API模板项目中,如果创建部分模拟并返回OK(someObject),则始终会收到一条错误消息,提示“值不能为null。 (参数“结果”)”
有趣的是,当我运行应用程序(F5)时,此方法工作正常。
不起作用的是使用Moq的单元测试。然后我得到这个错误。
我正在使用:
.NET Core 3.0(使用2.1时也会出现此错误)。
最新的Moq 4.13.1
最新的xunit 2.4.0
控制器:
library(tidyverse)
library(rvest)
dates <- seq.Date(as.Date("2015-01-01"),
as.Date("2015-01-05"), by = "day") #set to your range
result <- map(dates, ~read_lines(paste0("https://www.timeanddate.com/scripts/cityajax.php?n=spain/madrid&mode=historic&hd=",
format(., "%Y%m%d"),
"&month=",
format(., "%m"),
"&year=",
format(., "%Y")))) %>% #read files (html rather than json)
map(~paste0("<table>", . ,"</table>")) %>% #trick it into thinking it is a table
map(~read_html(.) %>% html_table()) %>% #extract table as dataframe
map2_df(dates, ~.x[[1]] %>%
set_names(make.names(.[1,], unique = TRUE)) %>% #sort out column names
select(-2) %>% #remove blank column
slice(2:(n() - 1)) %>% #remove first and last row
mutate(Date = .y)) %>% #add date column
mutate(Time = substr(Time, 1, 5)) #remove dates from time column
head(result)
Time Temp Weather Wind X.1 Humidity Barometer Visibility Date
1 07:00 -3 °C Clear. No wind <U+2191> 86% 1033 mbar 16 km 2015-01-01
2 07:30 -2 °C Clear. No wind <U+2191> 86% 1033 mbar 16 km 2015-01-01
3 08:00 -2 °C Clear. No wind <U+2191> 86% 1033 mbar 16 km 2015-01-01
4 08:30 -3 °C Clear. No wind <U+2191> 86% 1034 mbar 16 km 2015-01-01
5 09:00 -2 °C Sunny. No wind <U+2191> 93% 1034 mbar 16 km 2015-01-01
6 09:30 1 °C Sunny. No wind <U+2191> 75% 1035 mbar 16 km 2015-01-01
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace MyTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : BaseAccessController
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController()
{
}
[HttpGet]
public async Task<ActionResult<WeatherForecast>> Get()
{
try
{
int userId = 3;
if (HasAccess(userId) == false)
return Forbid();
var rng = new Random();
return Ok(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray()); // This is where the error happens!
}
catch (Exception ex)
{
throw;
}
}
}
}
单元测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace MyTest.Controllers
{
public class BaseAccessController : Controller
{
protected virtual bool HasAccess(int userId)
{
return true;
}
}
}
答案 0 :(得分:2)
这是XY problem。
首先,将操作定义为返回
public async Task<ActionResult<WeatherForecast>> Get()
还没有等待,它还会尝试返回WeatherForecast
return Ok(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray());
第二,如果使用ActionResult<T>
,则无需手动执行Ok(..)
结果。只需返回预期的对象。
所以我首先建议重构该动作以遵循文档中建议的语法
[HttpGet]
public async Task<ActionResult<WeatherForecast[]>> Get() { //Note the array
try {
int userId = 3;
if (HasAccess(userId) == false)
return Forbid();
var rng = new Random();
WeatherForecast[] results = await Task.Run(() => Enumerable
.Range(1, 5).Select(index => new WeatherForecast {
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray()
);
return results;
} catch (Exception ex) {
throw;
}
}
测试还应遵循正确的异步语法并声明预期的返回类型
[Fact]
public async Task MyUnitTest() {
// Arrange
var mockController = new Mock<WeatherForecastController>() {
CallBase = true; //so that it can call `HasAccess` without issue
};
// Act
ActionResult<WeatherForecast[]> actionResult = await mockController.Object.Get();
// Assert
Assert.IsNotNull(actionResult);
var returnValue = Assert.IsType<WeatherForecast>(actionResult.Value);
}
答案 1 :(得分:0)
我只需要将模拟控制器更改为此:
var mockController = new Mock<PersonsController>(mockPersonsService.Object) { CallBase = true };
现在可以正常工作。谢谢!! :-)