C# 单元测试依赖于其他公共方法的方法

C# 单元测试依赖于其他公共方法的方法,c#,unit-testing,mocking,C#,Unit Testing,Mocking,当我必须对调用其他类的多个公共方法的方法进行单元测试时,我感到非常困惑。这是一个例子 using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SkillKindle.BLL; using SkillKindle.BLL.ClassDetails; using SkillKindle.BLL.SkClasses; using SkillKindle.Doma

当我必须对调用其他类的多个公共方法的方法进行单元测试时,我感到非常困惑。这是一个例子

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SkillKindle.BLL;
using SkillKindle.BLL.ClassDetails;
using SkillKindle.BLL.SkClasses;
using SkillKindle.Domain.Models.SkClasses;
using SkillKindle.Infrastructure;
using SkillKindle.Web.Core.Infrastructure.ErrorHandling;
using SkillKindleWeb.Mappers;
using SkillKindleWeb.ViewModels.ClassDetails;

namespace SkillKindleWeb.Controllers
{
    [CustomHandleError(ExceptionType = typeof (BusinessValidationException))]
    public class ClassDetailsController : BaseController
    {
        private readonly ILogger _logger;
        private readonly IMapperService _mapperService;
        private readonly IAccessorFactory _accessorFactory;
        private const int RegistrationId = 34;

        private IClassDetailsAccessor ClassDetailsAccessor
        {
            get { return _accessorFactory.CreateClassDetailsAccessor(); }
        }

        private ISkClassAccessor SkClassAccessor
        {
            get { return _accessorFactory.CreateSkClassAccessor(); }
        }

        private IClassCreativeAccessor ClassCreativeAccessor
        {
            get { return _accessorFactory.CreateClassCreativeAccessor(); }
        }

        public ClassDetailsController(ILogger logger, IMapperService mapperService,
                                      IAccessorFactory accessorFactory)
        {
            _logger = logger;
            _mapperService = mapperService;
            _accessorFactory = accessorFactory;
        }

        public ViewResult Index(int classCreativeId)
        {
            var classCreative = ClassCreativeAccessor.GetClassCreative(classCreativeId);
            if (classCreative == null)
            {
                throw new HttpException(404, "The url is not valid");
            }

            var batches = ClassCreativeAccessor.GetFutureBatches(classCreativeId);
            IList<ClassTicket> tickets = new List<ClassTicket>();
            IList<Venue> venues = new List<Venue>();

            if (batches.Count > 0)
            {
                tickets =
                    ClassCreativeAccessor.GetTickets(
                        batches.Select(batch => batch.ClassScheduleId).Distinct().ToArray());
                venues = SkClassAccessor.GetVenues(batches.Select(batch => batch.VenueId).Distinct().ToArray());
            }

            var classDetailsViewModel = _mapperService.ClassCreativeToClassDetailsViewModel(classCreative);
            var batchViewModels = _mapperService.BatchToClassDetailsBatchViewModel(batches).ToList();
            var ticketViewModels = _mapperService.ClassTicketToClassDetailsTicketViewModel(tickets).ToList();
            var venueViewModels = _mapperService.VenueToClassDetailsVenueViewModel(venues).ToList();

            var indexViewModel = new IndexViewModel()
                {
                    Batches = batchViewModels,
                    Tickets = ticketViewModels,
                    ClassDetails = classDetailsViewModel,
                    Venues = venueViewModels
                };
            return View(indexViewModel);
        }
    }
}
使用System.Collections.Generic;
使用System.Linq;
使用System.Web;
使用System.Web.Mvc;
使用SkillKindle.BLL;
使用SkillKindle.BLL.ClassDetails;
使用SkillKindle.BLL.skclass;
使用SkillKindle.Domain.Models.SkClasses;
使用SkillKindle.Infrastructure;
使用SkillKindle.Web.Core.Infrastructure.ErrorHandling;
使用SkillKindleWeb.Mappers;
使用SkillKindleWeb.ViewModels.ClassDetails;
命名空间SkillKindleWeb.Controllers
{
[CustomHandleError(ExceptionType=typeof(BusinessValidationException))]
公共类ClassDetailsController:BaseController
{
专用只读ILogger\u记录器;
专用只读IMapperService\u mapperService;
专用只读IAccessorFactory\u accessorFactory;
私有const int RegistrationId=34;
专用IClassDetailsAccessor类详细信息Accessor
{
获取{return}accessorFactory.CreateClassDetailsAccessor();}
}
专用ISkClassAccessor SkClassAccessor
{
获取{return}accessorFactory.CreateSkClassAccessor();}
}
私人IClassCreativeAccessor类CreativeAccessor
{
获取{return}accessorFactory.CreateClassCreativeAccessor();}
}
公共类详细信息控制器(ILogger记录器、IMapperService mapperService、,
IAccessorFactory accessorFactory)
{
_记录器=记录器;
_mapperService=mapperService;
_accessorFactory=accessorFactory;
}
公共视图结果索引(int classCreativeId)
{
var classCreative=ClassCreativeAccessor.GetClassCreative(classCreativeId);
if(classCreative==null)
{
抛出新的HttpException(404,“url无效”);
}
var批次=ClassCreativeAccessor.GetFutureBatches(classCreativeId);
IList tickets=新列表();
IList场馆=新列表();
如果(batches.Count>0)
{
票=
ClassCreativeAccessor.GetTickets(
batches.Select(batch=>batch.ClassScheduleId.Distinct().ToArray());
Vincements=SkClassAccessor.getVincements(batches.Select(batch=>batch.VenueId).Distinct().ToArray());
}
var classDetailsViewModel=_mapperService.ClassCreativeToClassDetailsViewModel(classCreative);
var batchViewModels=_mapperService.BatchToClassDetailsBatchViewModel(batches.ToList();
var ticketViewModels=_mapperService.ClassTicketToClassDetailsTicketViewModel(tickets.ToList();
var venueViewModels=_mapperService.VenueToClassDetailsVenueViewModel(场馆).ToList();
var indexViewModel=新的indexViewModel()
{
批次=批次视图模型,
票证=票证视图模型,
ClassDetails=classDetailsViewModel,
场馆=venueViewModels
};
返回视图(indexViewModel);
}
}
}
这里索引方法依赖于mapperService、SkClassAccessor、ClassDetailsAccessor、, ClassCreativeAccessor公共方法。我已经分别对这些公共方法进行了单元测试。现在,当谈到测试索引方法时,我需要检查indexViewModel的正确性。我有几个选择

备选案文1。模拟依赖类的公共方法以返回假对象,并检查IndexViewModel是否具有这些假对象。我不确定这是否是一个真正的测试。它也没有测试我是否将写参数传递给这些模拟公共方法

备选案文2。不要模仿依赖类的公共方法,而是假装依赖类的依赖关系。e、 伪造罚单ClassCreativeAccessor.GetTickets将对其进行操作。这种方法将验证我是否将正确的参数传递给依赖的公共方法。但在这里,我将再次测试公共方法

我不确定哪种方法是正确的。谢谢你的帮助

我不确定这是否是一个真正的测试

这应该是一个单元测试。不要把它和集成测试混为一谈

它也不测试我是否将写参数传递给这些 模拟公共方法

在模拟依赖项(第一个选项)时,始终可以验证是否使用适当的参数调用了方法。例如,对于最小起订量:

mock.Verify(foo => foo.Execute("ping"));
将检查是否使用参数“ping”调用了依赖项
foo
的方法
Execute
。同样,您可以验证是否使用适当的参数调用了
ClassCreativeAccessor

int classCreativeId = 42;
List<Batch> batches = new List<Batch>();

creativeAccessorMock.Setup(ca => ca.GetFutureBatches(classCreativeId))
                    .Returns(batches);
...
int classCreativeId=42;
列表批次=新列表();
creativeAccessorMock.Setup(ca=>ca.GetFutureBacks(classCreativeId))
.退货(批次);
...

我认为您必须使用第一种方法,因为再次测试所有方法是没有意义的,因为如果您要更改函数的功能,您必须修复多个单元测试,这不是最佳做法,我想您是对的。我已经按照您的建议编写了单元测试。非常感谢你。