我认为我必须通过传递我的上下文实例和我的服务来创建我的控制器以使其可测试吗?
例如:new Controller(mycontext,myservice)
我认为这是我需要更改代码的方式,但如果我不需要,我不想这样做。由于MVC3开箱即用,它需要控制器构造器无参数,我认为这意味着我将不得不沿着IoC的道路走下去。否则,我的向导操作中的代码即使在测试期间也会保存到真正的DBContext。
namespace mvc3test.Controllers
{
public class WizardController : Controller
{
private DR405DBContext db;
public WizardController(DR405DBContext dbContext)
{
db = dbContext;
}
public WizardController()
{
db = new DR405DBContext();
}
public ActionResult Index()
{
var model = new WizardViewModel();
model.Initialize();
return View(model);
}
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard)
{
//wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request.QueryString["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Review", wizard);
}
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
public ActionResult Review(int id)
{
var service = new DR405Service();
var dr405 = service.GetDR405ById(db, id);
var wizard = new WizardViewModel();
if (dr405 != null)
{
wizard.Initialize();
foreach (var s in wizard.Steps)
{
Mapper.Map(dr405, s, typeof(dr405), s.GetType());
}
}
return View(wizard);
}
public ActionResult Transmit()
{
return View();
}
[HttpPost]
public String Upload(HttpPostedFileBase FileData)
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TaxPayerID);
System.IO.Directory.CreateDirectory(saveLocation);
FileData.SaveAs(Path.Combine(saveLocation, FileData.FileName));
ViewBag.Message = String.Format("File name: {0}, {1}Kb Uploaded Successfully.", FileData.FileName, (int)FileData.ContentLength / 1024);
return ViewBag.Message;
}
}
}
答案 0 :(得分:2)
我认为我必须通过传递我的上下文实例和我的服务来创建我的控制器以使其可测试吗?
有点儿。这只是你需要完成的工作的一半。下半部分是通过使用抽象来削弱图层之间的耦合。您的服务层需要实现一个接口,您将注入到控制器的构造函数中,强制执行这两者之间的契约,并明确声明控制器需要服务层遵守此合同:
public WizardController(IMyService service)
{
this._service = service;
}
现在,在您的单元测试中,使用其中一个模拟框架(mock it,Rhino Mocks,NSubstitute,Moq继续进行NMock, ...)。
答案 1 :(得分:1)
您可以在Controller上使用setter注入而不是构造函数注入。
public class WizardController : Controller
{
public void setDBContext( DR405DBContext db) {
this.db = db;
}
}
或
您可以使用服务定位器获取数据库,并为其添加一个setter。
public class DBServiceLocator
{
private static DR405DBContext db = new DR405DBContext();
public static DR405DBContext locate() {
return db;
}
public static setContext(DR405DBContext db) {
DBServiceLocator.db = db;
}
}
在单元测试的setup()部分中,使用setter来“注入”模拟/存根数据库。
此外,使用界面而不是DR405DBContext将使模拟更容易。