1

(编辑**我能够获得代码来编译并执行通过的单元测试。除了代码修复之外,VS2010无限期运行单元测试存在问题。我不得不替换一个已更改的dll文件在中止安装 vs 2012 期间。我在页面底部发布了对控制器和单元测试的更改。感谢所有发布答案的人。)

这是我在网上问过的第一个关于编码的问题。大约一年来,我一直在使用免费教程学习 C# .NET 和其他相关内容。到目前为止,我已经能够自己研究和解决所有问题。我现在开始冒险进入未知领域,我似乎找不到答案。

我一直在编写一个名为“在 7 天内逐步学习 MVC 模型视图控制器”的教程。这是链接: http: //www.codeproject.com/Articles/259560/Learn-MVC-Model-view-controller-Step-by-Step-in-7

我已经研究了错误的建议链接:

Error 'Mvccustomer.Models.Customer' does not contain a definition for 'DisplayCustomer'        and no extension method 'DisplayCustomer' accepting a first argument of type     'Mvccustomer.Models.Customer' could be found (are you missing a using directive or an     assembly reference?)

我遇到的问题是我似乎找不到类似的情况,有人正在使用类似的文件引用创建单元测试。请注意,我对 MVC 和单元测试完全陌生。

本教程的一个问题是,作者在视频中使用了一组命名空间/文件名,而在书面教程中使用了另一组。我能够自己解决这个问题。例如,一开始,他使用“Mvccustomer”作为项目名称,但在第一天的第 4 或第 5 个实验室时,他将其称为“Mvcinputscreen”。我怀疑问题在于项目中如何引用客户类,但到目前为止我无法弄清楚。

这是给我一个错误的单元测试:

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Mvccustomer.Models;

namespace MvcUnitTest
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void DisplayCustomer()
        {
            Customer obj = new Customer();
            var varresult = obj.DisplayCustomer();
            Assert.AreEqual("DisplayCustomer", varresult.ViewName);
        }
    }
}

这是客户类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Mvccustomer.Models;

namespace Mvccustomer.Models
{
    public class Customer
    {
        public int Id { set; get; }
        public string CustomerCode { set; get; }
        public double Amount { set; get; }
    }
}

这是显示客户视图:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Mvccustomer.Models.Customer>"     %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>DisplayCustomer</title>
</head>
<body>
    <div>
    The customer id is <%= Model.Id %> <br />

    The customer id is <%= Model.CustomerCode %> <br />

    <%if (Model.Amount > 100)
      {%>
    This is a priveleged customer.
    <% }
      else
      { %>
    This is a normal customer
    <%} %>

    </div>
</body>
</html>

和客户控制器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvccustomer.Models;


namespace Mvccustomer.Controllers
{
    public class CustomerController : Controller
{
    //
    // GET: /Customer/

    public ActionResult Index()
    {
        return View();
    }

    public ActionResult FillCustomer()
    {
        return View();
    }
    public ActionResult DisplayCustomer(Customer obj)
    {
        return View(obj);
    }

}

}

如果我需要发布更多项目元素,请告诉我。当我构建 Mvccustomer 项目时,它编译得很好,没有错误。只有单元测试给我带来了麻烦。我想这个问题有点复杂,我热切地等待所有建设性批评带来的学习体验。谢谢你。

最终工作的编辑控制器和单元测试:

客户控制器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvccustomer.Models;
using Mvccustomer.Controllers;

namespace Mvccustomer.Controllers
{
public class CustomerController : Controller
{
    //
    // GET: /Customer/

    public ActionResult Index()
    {
        return View();
    }

    public ActionResult FillCustomer()
    {
        return View();
    }

    [HttpPost]
    public ActionResult DisplayCustomerView(CustomerModel customerModel)
    {
      var myView = View("DisplayCustomerView", customerModel);
      //myView.ViewName = "DisplayCustomer";
      return myView;
    }

}
}

编辑单元测试:

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Mvccustomer.Models;
using Mvccustomer.Controllers;
using System.Web.Mvc;

namespace MvcUnitTest
{


[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void DisplayCustomer()
    {
      // instantiates new instance of CustomerController class
      CustomerController controller = new CustomerController();

      var customer = new CustomerModel();

      var customerViewActionResult = controller.DisplayCustomerView(customer);
      var customerViewViewResult = customerViewActionResult as ViewResult;


      Assert.AreEqual("DisplayCustomerView", customerViewViewResult.ViewName);

    }
}
}
4

4 回答 4

6

您正在测试中调用obj.DisplayCustomer()对象Customer中的方法。但是,我DisplayCustomerCustomer.

该类CustomerController有一个方法DisplayCustomer,但这个方法需要一个 type 参数Customer

CustomerController obj = new CustomerController();
var varresult = obj.DisplayCustomer(new Customer());

如果智能感知没有显示该方法,可能是因为该方法不存在或者是私有的。

于 2012-11-14T12:43:41.130 回答
2
CustomerController obj = new CustomerController();
var result = obj.DisplayCustomer(new Customer()) **as ViewResult**;
Assert.AreEqual("Expected", result.ViewName);
于 2012-11-14T13:11:13.430 回答
1

您正在测试应该由控制器返回的视图名称,但您没有创建控制器。相反,您正在调用not .DisplayCustomer()theCustomerCustomerController

我也会让你的代码更具解释性。单元测试功能的名称应该解释他们正在测试什么。变量名应该解释它们是什么(这obj是一个坏名字,因为它没有意义)。

考虑阅读另一个程序员的代码并需要了解它是如何工作的,或者在 2 年后回到你自己的代码并尝试记住它是如何工作的。给事物起解释性的名称会有所帮助。我会像这样重写你的测试:

    [TestMethod]
    public void DisplayCustomer_ReturnsViewNamed_DisplayCustomer()
    {
        const string expectedViewName = "DisplayCustomer";
        var customer = new Customer();
        var controllerUnderTest = new CustomerController();

        var result = controllerUnderTest.DisplayCustomer(customer);

        Assert.AreEqual(expectedViewName, result.ViewName);
    }

如果你喜欢阅读编程书籍,我强烈推荐Robert Martin 的Clean Code。它是用 Java 编写的代码示例,但 Java 在语法上接近于 C#,它是一本关于保持代码可读、简单和组织良好的好书。我保留了我在编码时经常参考的书中的笔记备忘单。


编辑:

关于您的新错误:

“System.Web.Mvc.ActionResult”不包含“ViewName”的定义,并且找不到接受“System.Web.Mvc.ActionResult”类型的第一个参数的扩展方法“ViewName”(您是否缺少 using 指令还是汇编参考?)

控制器上的方法签名是:

public ActionResult DisplayCustomer(Customer obj)

它返回一个ActionResult对象。.ViewName该类型不存在该属性,它实际上是 的一个属性ViewResult

控制器中方法内的行DisplayCustomer()返回:

return View(obj);

View()方法实际上返回 a ViewResult,但ViewResult该类扩展了ActionResult

public class ViewResult : ActionResult

因此,可以将您的方法签名设置为,ActionResult但实际上您总是返回 a ViewResult,因为ViewResult is an ActionResult。(典型的面向对象继承的东西,但希望这部分到目前为止是有意义的)。

在您的测试中,当您调用DisplayCustomer()测试所知道的所有内容时,它所要做的就是方法签名,它告诉编译器它将返回一个ActionResult. 因此,编译器试图在不存在ViewName的类上查找属性名称。Actionresult

有两种方法可以解决此问题:

一种是简单地将结果投射到您的测试中:

var varresult = (ViewResult)obj.DisplayCustomer();

或者,由于该方法始终返回 aViewResult您可以更改方法签名以指示它返回此更具体的类型:

public ViewResult DisplayCustomer(Customer obj)
{
    return View(obj);
}
于 2012-11-14T13:07:58.327 回答
1

为了简单起见,我试图将其归结为不使用模拟框架,这将消除或减少对try/catch(取决于您自己的偏好/风格)的需求。

刚刚注意到您遇到问题的原因之一,该方法DisplayCustomer()应该返回ViewResultnot ActionResult

但是本教程仍然不起作用,因为在ExecuteResult()调用之前不会发生视图名称。

   [TestMethod]
    public void DisplayCustomerTest_FindsCorrectViewName()
    {
        var expected = "DisplayCustomer";

        var obj = new CustomerController();
        var cContext = new ControllerContext();

        cContext.RouteData.Values.Add("action", expected);
        cContext.RouteData.Values.Add("controller", "Customer");

        var actionResult = obj.DisplayCustomer(new Customer());

        //not necessary but helpful
        Assert.IsInstanceOfType(actionResult, typeof(ViewResult));

        //down cast
        var vResult = actionResult as ViewResult;

        try // the view name is populated early, and we don't care about what else it does
        {
            vResult.ExecuteResult(cContext);
        }
        catch (NotImplementedException) {} //catch the most specific error type you can

        Assert.AreEqual(expected, vResult.ViewName);

    }

更多有趣的方法,或更高级的(意见问题,并且似乎缺少使用适当的模拟框架的任何答案)

获取视图名称,其中 ViewResult.ViewName 是单元测试的空字符串

低调的解释

请原谅造型,家里没有安装 Resharper 或 StyleCop。

对于单元测试命名约定,请考虑单元测试命名最佳实践

于 2012-12-17T18:20:47.940 回答