如何在C#中为create方法编写单元测试?

如何在C#中为create方法编写单元测试?,c#,.net,entity-framework,unit-testing,moq,C#,.net,Entity Framework,Unit Testing,Moq,我想在C#中为add方法编写单元测试。我的方法得到一个dog实体和类型作为参数。这个方法通过我的服务将它添加到数据库中 public async Task<ActionResult> Add(Dog dog, string type) { if (ModelState.IsValid) { var d = new Dog(); d.Name = dog.Name; d

我想在C#中为add方法编写单元测试。我的方法得到一个dog实体和类型作为参数。这个方法通过我的服务将它添加到数据库中

  public async Task<ActionResult> Add(Dog dog, string type)
    {
        if (ModelState.IsValid)
        {
            var d = new Dog();

            d.Name = dog.Name;
            d.NumOfLegs = dog.NumOfLegs;
            d.BirthdayDate = dog.BirthdayDate;

            if(type == "mom"){
              //when the dog is a mom, dog.Childrens got default puppies
              InitChildrenOfMomDog(dog);
            }

            dogService.Insert(dog);

            return RedirectToAction("Home");
        }

        return View(dog);
    }
公共异步任务添加(Dog-Dog,字符串类型)
{
if(ModelState.IsValid)
{
var d=新狗();
d、 Name=dog.Name;
d、 NumOfLegs=dog.NumOfLegs;
d、 BirthdayDate=dog.BirthdayDate;
如果(类型=“妈妈”){
//当狗是妈妈的时候,狗。孩子们有默认的小狗
InitChildrenOfMomDog(狗);
}
dogService.Insert(dog);
返回操作(“主页”);
}
返回视图(dog);
}

我想签入单元测试,我的方法工作正常,默认小狗添加ok到狗,或者如果用户添加了有效(或无效)属性。。。在这一点上我有点困惑。

您应该将控制器的代码重构为一个单独的视图模型类。这允许在不与MVC框架交互的情况下直接测试视图模型。看看这个模式

控制器 创建、生成并返回视图模型类。这里应该只有专门限于视图的逻辑,如果有的话,因为它不可直接测试

视图模型 执行视图的所有逻辑操作,例如数据库查询。可以直接测试


代码示例 控制器

public async Task<ActionResult> Add(Dog dog, string type)
{
    if (!ModelState.IsValid)
        return View(dog);

    var vm = new AddDogVM(dogService);
    vm.Add(dog, type);

    return RedirectToAction("Home");
}
公共异步任务添加(Dog-Dog,字符串类型)
{
如果(!ModelState.IsValid)
返回视图(dog);
var vm=新的AddDogVM(dogService);
添加(狗,类型);
返回操作(“主页”);
}
查看模型类

public class AddDogVM
{
    private IDogService _dogService

    public AddDogVM(IDogService dogService)
    {
        _dogService = dogService;
    }

    public async Task<ActionResult> Add(Dog dog, string type)
    {
        var d = new Dog();

        d.Name = dog.Name;
        d.NumOfLegs = dog.NumOfLegs;
        d.BirthdayDate = dog.BirthdayDate;

        if (type == "mom") {
            InitChildrenOfMomDog(dog);
        }

        _dogService.Insert(dog);
    }
}
公共类AddDogVM
{
私人IDogService\u dogService
公共AddDogVM(IDogService dogService)
{
_dogService=dogService;
}
公共异步任务添加(狗,字符串类型)
{
var d=新狗();
d、 Name=dog.Name;
d、 NumOfLegs=dog.NumOfLegs;
d、 BirthdayDate=dog.BirthdayDate;
如果(类型=“妈妈”){
InitChildrenOfMomDog(狗);
}
_dogService.Insert(dog);
}
}
测试

public void Test()
{
    var dogService = SomeMockingFramework.CreateSubstituteFor<IDogService>();
    var vm = new AddDogVM(dogService);
    vm.Add(...);

    // Assertions
}
公共无效测试()
{
var dogService=SomeMockingFramework.CreateSubstituteFor();
var vm=新的AddDogVM(dogService);
vm.Add(…);
//断言
}

从技术上讲,纯粹的“单元”测试很可能不会测试依赖于Mvc框架的函数-您可能希望了解如何在
IsValid
return
之间提取代码(并了解如何模拟
dogService
)。或者你想要一个集成测试来模拟当web客户端调用这个API时会发生什么?是的,我想了解mocking,我读过它,在这种情况下它会很好,但是我很困惑,我应该怎么做。这真的取决于你使用的平台。但是,只要将
dogService
声明为某种类型的接口,可以用公开相同函数的模拟类型替换,就不难了。顺便说一句,你是否真的从一个网络客户端测试过它,并确认它正在数据库中存储新的狗?正常情况下,我希望在某处看到一个
SaveChanges()
调用(但可能在“Insert”函数中)。我检查了它,并正确地保存到了数据库中。正如您所说,服务是项目中的一个接口。
dogService
未在VM中声明。它应该被注射。它是如此的清晰和易懂!非常感谢你!谢谢@OlivierJacot Descombes,我完全忽略了这项服务。