C# ASP.NET MVC 控制器单元测试

3

我对单元测试还比较新,我想知道我是否做得正确。

//Controller
public ActionResult Index()
{
    return View("../Message/Index");
}

[TestMethod]
public void MessageViewCorrectTest()
{
    var controller = new MessageController();
    var result = controller.Index() as ViewResult;
    Assert.AreEqual("../Message/Index", result.ViewName);
}

[TestMethod]
public void MessageViewInCorrectTest()
{
    var controller = new MessageController();
    var result = controller.Index() as ViewResult;
    Assert.AreNotEqual("Something/Else", result.ViewName);
}

我这样做对吗?有更好的方法还是这样就可以了?

感谢提供任何反馈意见。

4个回答

2
这里有一种方法可以实现。您还可以根据您的模型类型进行验证。
[TestMethod]
public void TestMethod2()
{
  MessageController controller = new MessageController();
  ActionResult result = controller.Index(1);
  Assert.IsInstanceOfType(result, typeof(ViewResult));
  //Since view has been asserted as ViewResult
  ViewResult viewResult = result as ViewResult;  
  if(viewResult != null)
  {      
     Assert.IsInstanceOfType(viewResult.Model, typeof(YourModelType));
    //Further Asserts for your model 
  } 
}

2

这是我一直在使用的单元测试结构。它基于我在LosTechies上找到的Jimmy Bogard的SpecificationBase类。

好处在于每个场景都封装成自己的类。然后你读测试时听起来很自然。

这假设使用NUnit和FakeItEasy,但可以修改为MS-TEST。

[TestFixture]
public abstract class SpecificationBase
{
    [SetUp]
    public void SetUp()
    {
        Given();
        When();
    }

    protected virtual void Given() { }
    protected virtual void When() { }
}

public class ThenAttribute : TestAttribute { }

下面是实际的控制器测试

public static class DataControllerTests
{
    public class WhenViewingWesternRegionLoadLookAhead : SpecificationBase
    {
        private DataController _sut;
        private ViewResult _result;
        private IProvideeDataFeedData _eDataProvider;

        protected override void Given()
        {
            _eDataProvider = A.Fake<IProvideeDataFeedData>();
            A.CallTo(() => _eDataProvider.GetAllDayAheadLoad()).Returns(new collectionActualValueData
            {
                timestamp = new DateTime(2015, 5, 5),
                timestampSpecified = true,
                actualValueData = new[]
                {
                    new actualValueData {value = 0.1f, name = "Western Region", timestamp = new DateTime(2015, 5, 5)},
                    new actualValueData {value = 0.1f, name = "some region", timestamp = new DateTime(2015, 5, 5)}
                }
            });

            _sut = new DataController(_eDataProvider);
        }

        protected override void When()
        {
            _result = (ViewResult)_sut.Index();
        }

        [Then]
        public void ViewNameShouldBeCorrect()
        {
            Assert.That(_result.ViewName, Is.EqualTo(""));
        }

        [Then]
        public void ModelShouldBeCorrectType()
        {
            Assert.That(_result.Model.GetType(), Is.EqualTo(typeof(IndexModel)));
        }

        [Then]
        public void GetAllDayAheadLoadShouldBeCalledOnce()
        {
            A.CallTo(() => _eDataProvider.GetAllDayAheadLoad()).MustHaveHappened(Repeated.Exactly.Once);
        }           
    }

    public class WhenViewingWesternRegionLoadLookAheadAndValuesAreUnder50000 : SpecificationBase
    {
        private DataController _sut;
        private ViewResult _result;
        private IndexModel _expectedData;
        private IProvideeDataFeedData _eDataProvider;

        protected override void Given()
        {
            _expectedData = new IndexModel
            {
                Message = "Everything is cool",
                Region = "Western Region",
                Values = new Dictionary<DateTime, float>
                {
                    {new DateTime(2015, 5, 5), 0.1f}
                }
            };


            _eDataProvider = A.Fake<IProvideeDataFeedData>();
            A.CallTo(() => _eDataProvider.GetAllDayAheadLoad()).Returns(new collectionActualValueData
            {
                timestamp = new DateTime(2015, 5, 5),
                timestampSpecified = true,
                actualValueData = new[]
                {
                    new actualValueData {value = 0.1f, name = "Western Region", timestamp = new DateTime(2015, 5, 5)},
                    new actualValueData {value = 0.1f, name = "some region", timestamp = new DateTime(2015, 5, 5)}
                }
            });

            _sut = new DataController(_eDataProvider);
        }

        protected override void When()
        {
           _result = (ViewResult)_sut.Index();
        }

        [Then]
        public void ModelDataShouldBeCorrect()
        {
            var model = (IndexModel)_result.Model;

            Assert.That(model.Message, Is.EqualTo(_expectedData.Message));
            Assert.That(model.Region, Is.EqualTo(_expectedData.Region));
            Assert.That(model.Values, Is.EquivalentTo(_expectedData.Values));
        }
    }

    public class WhenViewingWesternRegionLoadLookAheadAndValuesAreOver50000 : SpecificationBase
    {
        private DataController _sut;
        private ViewResult _result;
        private IndexModel _expectedData;
        private IProvideeDataFeedData _eDataProvider;

        protected override void Given()
        {
            _expectedData = new IndexModel
            {
                Message = "Heavy Load",
                Region = "Western Region",
                Values = new Dictionary<DateTime, float>
                {
                    {new DateTime(2015, 5, 5), 51000f}
                }
            };

            _eDataProvider = A.Fake<IProvideeDataFeedData>();
            A.CallTo(() => _eDataProvider.GetAllDayAheadLoad()).Returns(new collectionActualValueData
            {
                timestamp = new DateTime(2015, 5, 5),
                timestampSpecified = true,
                actualValueData = new[]
                {
                    new actualValueData {value = 51000f, name = "Western Region", timestamp = new DateTime(2015, 5, 5)},
                    new actualValueData {value = 0.1f, name = "some region", timestamp = new DateTime(2015, 5, 5)}
                }
            });

            _sut = new DataController(_eDataProvider);
        }

        protected override void When()
        {
            _result = (ViewResult)_sut.Index();
        }

        [Then]
        public void ModelDataShouldBeCorrect()
        {
            //Assert.That(_result.Model, Is.EqualTo(_expectedData));
            var model = (IndexModel) _result.Model;

            Assert.That(model.Message, Is.EqualTo(_expectedData.Message));
            Assert.That(model.Region, Is.EqualTo(_expectedData.Region));
            Assert.That(model.Values, Is.EquivalentTo(_expectedData.Values));

        }
    }
}

这里是正在测试的控制器

public class DataController : Controller
{
    private readonly IProvideeDataFeedData _eDataFeedDataProvider;

    public DataController(IProvideeDataFeedData eDataFeedDataProvider)
    {
        _eDataFeedDataProvider = eDataFeedDataProvider;
    }

    public ActionResult Index()
    {
        var values = _eDataFeedDataProvider.GetAllDayAheadLoad().actualValueData
            .Where(a => a.name == "Western Region")
            .ToDictionary(a => a.timestamp, a => a.value);

        var model = new IndexModel
        {
            Region = "Western Region",
            Message = values.Any(v => v.Value > 50000) ? "Heavy Load" : "Everything is cool",
            Values = values
        };

        return View(model);
    }
}

1

我非常推荐你使用FluentmvcTesting

    [Test]
    public void Render_default_view_for_get_to_index()
    {
        _controller.WithCallTo(c => c.Index())
            .ShouldRenderDefaultView();
    }

你会发现

exemples


0

我不知道我是否离题了,但是:你确定需要测试你的控制器吗? 通常情况下,我会遵循“Fat Model,Skinny Controller”的指导方针(将Model视为一个经过适当设计的项目),并将Controller限制为解析请求。 这应该让你留下微不足道的代码,我不会费心测试它。

换句话说,我不会费心测试任何返回ActionResult的方法。

如果您的控制器中有算法代码,并且您想要测试它,请尝试将其重构到操作方法之外,以便隔离测试算法部分。

单元测试很好,但昂贵,而且它不是一个万能药:如果您的代码足够简单,我主张您可以不进行单元测试。

想一想:你在测试什么?如果它是可以重构或拆分的东西,那么无论如何都要进行单元测试。但是,如果真的没有什么可以分解的东西,请不要浪费时间在控制器上,并为您的模型设置适当的测试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接