如何使用 Moq 对具有三个依赖项的 MVC 控制器进行单元测试
How to Unit Test an MVC Controller with Three Dependencies, Using Moq
警告:一般来说,我是 Moq、Moq 单元测试和 TDD 的新手。
我有一个具有三个依赖项的控制器。这是 SomethingController 的构造函数(等):
public class SomethingController : Controller
{
private readonly ILogger<SomethingController> _logger;
private readonly ISomethingRepository _something;
private readonly IExceptionSvc _ex;
public SomethingController(ILogger<SomethingController> logger,
ISomethingRepository something,
IExceptionSvc ex)
{
_logger = logger;
_something = something;
_ex = ex;
}
[HttpGet]
[AllowAnonymous]
public JsonResult GetStuff()
{
//intitialize list
var stuffs = new List<StuffViewModel>();
try
{
_logger.LogInformation("SomethingController: GetStuff() - Getting Stuff, sorted ascending.");
//get the stuffs
stuffs = _something.GetStuff();
_logger.LogInformation("Retrieved {0} Stuffs.", stuffs.Count);
}
catch (Exception ex)
{
_logger.LogError("Error in SomethingController: GetStuff()", ex);
return _ex.Http500ErrorReturn("GetStuff");
}
return Json(stuffs);
}
}
现在,这里是每个依赖项的描述:
ILogger:这只是用于日志记录,通过 NLog 使用 Microsoft.Extensions.Logging。
ISomethingRepository:这完成了所有工作(好吧......它的实现,真的)。它调用数据库并获取东西。有一个名为 GetStuff() 的方法,它只是 return 一个 Stuff 列表。这个 Repository 依赖于数据库,它通过构造函数注入到 repo 中。
IExceptionSvc:这只是一个愚蠢的小服务,它有一个方法 return 对 JSON 格式的 return 的 500 错误响应] 给来电者。
我想测试 Controller 中的 GetStuff() 操作,它将调用 Repository 中的 GetStuff() 实现。
我目前在我的测试项目中插入了以下代码:
public void GetStuff_Is_Awesome()
{
Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);
sc.GetStuff();
//...what now? What am I looking for? Am I going to see a list of stuffs here?
}
我只想知道会发生什么?我是否在测试 Stuffs 列表是否被 returned?我该如何测试?
这是 Repository 中 GetStuff() 的实现(带有 repo 构造函数以供参考):
private readonly ApplicationDbContext _context;
private readonly ILogger<SomethingRepository> _logger;
public SomethingRepository(ApplicationDbContext context,
ILogger<SomethingRepository> logger)
{
_context = context;
_logger = logger;
}
public List<StuffViewModel> GetStuff()
{
List<StuffViewModel> stuffs = null;
stuffs = _context.Stuffs.OrderBy(b => b.Name).Select(b => new StuffViewModel
{
Id = b.Id,
Name = b.Name
}).ToList();
return stuffs;
}
将其视为安排,执行断言
public void GetStuff_Is_Awesome()
{
//arrange
Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);
//act
sc.GetStuff();
//assert
}
因此,您想断言发生了某事,或者调用了某事。
在不了解您的域或 ISomethingRepository 的更多信息的情况下,您可以断言该方法已被调用:
something.Verify(m => m.MethodToCheckIfCalled());
其他选项是为 MethodToCheckIfCalled
设置 return 值并断言结果等...
假设您的控制器上有一个 GetStuff
方法,如下所示:
public ActionResult GetStuff()
{
var data = _something.GetFromRepo();
return View(data);
}
我会做类似的事情:
public void GetStuff_Is_Awesome() //please don't call it this
{
//arrange
Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);
//setup your something mock GetFromRepo method to return a List of StuffViewModel
something.Setup(m=> m.GetFromRepo())
.Returns(new List<StuffViewModel>(){Id = 1, Name = "Test"});
//act
var result = sc.GetStuff();
//assert
//safely cast result to ViewResult
var viewResult = result as ViewResult;
Assert.IsNotNull(viewResult);
Assert.IsNotNull(viewResult.Model);
// add additional checks on the Model.. loads of ways of doing this
var viewResultModel = viewResut.Model as List<StuffViewModel>;
Assert.AreEqual(1, viewResultModel.First().Id);
Assert.AreEqual("Test", viewResultModel.First().Name);
}
警告:一般来说,我是 Moq、Moq 单元测试和 TDD 的新手。
我有一个具有三个依赖项的控制器。这是 SomethingController 的构造函数(等):
public class SomethingController : Controller
{
private readonly ILogger<SomethingController> _logger;
private readonly ISomethingRepository _something;
private readonly IExceptionSvc _ex;
public SomethingController(ILogger<SomethingController> logger,
ISomethingRepository something,
IExceptionSvc ex)
{
_logger = logger;
_something = something;
_ex = ex;
}
[HttpGet]
[AllowAnonymous]
public JsonResult GetStuff()
{
//intitialize list
var stuffs = new List<StuffViewModel>();
try
{
_logger.LogInformation("SomethingController: GetStuff() - Getting Stuff, sorted ascending.");
//get the stuffs
stuffs = _something.GetStuff();
_logger.LogInformation("Retrieved {0} Stuffs.", stuffs.Count);
}
catch (Exception ex)
{
_logger.LogError("Error in SomethingController: GetStuff()", ex);
return _ex.Http500ErrorReturn("GetStuff");
}
return Json(stuffs);
}
}
现在,这里是每个依赖项的描述:
ILogger:这只是用于日志记录,通过 NLog 使用 Microsoft.Extensions.Logging。
ISomethingRepository:这完成了所有工作(好吧......它的实现,真的)。它调用数据库并获取东西。有一个名为 GetStuff() 的方法,它只是 return 一个 Stuff 列表。这个 Repository 依赖于数据库,它通过构造函数注入到 repo 中。
IExceptionSvc:这只是一个愚蠢的小服务,它有一个方法 return 对 JSON 格式的 return 的 500 错误响应] 给来电者。
我想测试 Controller 中的 GetStuff() 操作,它将调用 Repository 中的 GetStuff() 实现。
我目前在我的测试项目中插入了以下代码:
public void GetStuff_Is_Awesome()
{
Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);
sc.GetStuff();
//...what now? What am I looking for? Am I going to see a list of stuffs here?
}
我只想知道会发生什么?我是否在测试 Stuffs 列表是否被 returned?我该如何测试?
这是 Repository 中 GetStuff() 的实现(带有 repo 构造函数以供参考):
private readonly ApplicationDbContext _context;
private readonly ILogger<SomethingRepository> _logger;
public SomethingRepository(ApplicationDbContext context,
ILogger<SomethingRepository> logger)
{
_context = context;
_logger = logger;
}
public List<StuffViewModel> GetStuff()
{
List<StuffViewModel> stuffs = null;
stuffs = _context.Stuffs.OrderBy(b => b.Name).Select(b => new StuffViewModel
{
Id = b.Id,
Name = b.Name
}).ToList();
return stuffs;
}
将其视为安排,执行断言
public void GetStuff_Is_Awesome()
{
//arrange
Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);
//act
sc.GetStuff();
//assert
}
因此,您想断言发生了某事,或者调用了某事。
在不了解您的域或 ISomethingRepository 的更多信息的情况下,您可以断言该方法已被调用:
something.Verify(m => m.MethodToCheckIfCalled());
其他选项是为 MethodToCheckIfCalled
设置 return 值并断言结果等...
假设您的控制器上有一个 GetStuff
方法,如下所示:
public ActionResult GetStuff()
{
var data = _something.GetFromRepo();
return View(data);
}
我会做类似的事情:
public void GetStuff_Is_Awesome() //please don't call it this
{
//arrange
Mock<ILogger<SomethingController>> logger = new Mock<ILogger<SomethingController>>();
Mock<ISomethingRepository> something = new Mock<ISomethingRepository>();
Mock <IExceptionSvc> ex = new Mock<IExceptionSvc>();
SomethingController sc = new SomethingController(logger.Object, something.Object, ex.Object);
//setup your something mock GetFromRepo method to return a List of StuffViewModel
something.Setup(m=> m.GetFromRepo())
.Returns(new List<StuffViewModel>(){Id = 1, Name = "Test"});
//act
var result = sc.GetStuff();
//assert
//safely cast result to ViewResult
var viewResult = result as ViewResult;
Assert.IsNotNull(viewResult);
Assert.IsNotNull(viewResult.Model);
// add additional checks on the Model.. loads of ways of doing this
var viewResultModel = viewResut.Model as List<StuffViewModel>;
Assert.AreEqual(1, viewResultModel.First().Id);
Assert.AreEqual("Test", viewResultModel.First().Name);
}