Asp.net mvc 我不熟悉单元测试,我';I’我想知道为什么它不起作用

Asp.net mvc 我不熟悉单元测试,我';I’我想知道为什么它不起作用,asp.net-mvc,unit-testing,xunit,Asp.net Mvc,Unit Testing,Xunit,我是MVC和单元测试的新手,所以我一直在遵循指南等 目前,我正在研究单元测试。我有一个测试,据我所知应该是有效的,但不幸的是没有 using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using WorkingWithVisualStudio.Controllers.Home; using WorkingWithVisualStudio.Models; using Xunit; namespace Workin

我是MVC和单元测试的新手,所以我一直在遵循指南等

目前,我正在研究单元测试。我有一个测试,据我所知应该是有效的,但不幸的是没有

    using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using WorkingWithVisualStudio.Controllers.Home;
using WorkingWithVisualStudio.Models;
using Xunit;
namespace WorkingWithVisualStudio.Tests
{
    public class HomeControllerTests
    {
        class ModelCompleteFakeRepository : IRepository
        {
            public IEnumerable<Product> Products { get; } = new Product[] {
 new Product { Name = "P1", Price = 275M },
 new Product { Name = "P2", Price = 48.95M },
 new Product { Name = "P3", Price = 19.50M },
 new Product { Name = "P3", Price = 34.95M }};
            public void AddProduct(Product p)
            {
                // do nothing - not required for test
            }
        }
        [Fact]
        public void IndexActionModelIsComplete()
        {
            // Arrange
            var controller = new HomeController();
            controller.Repository = new ModelCompleteFakeRepository();

            // Act
            var model = (controller.Index() as ViewResult)?.ViewData.Model
            as IEnumerable<Product>;
            // Assert
            Assert.Equal(controller.Repository.Products, model,
            Comparer.Get<Product>((p1, p2) => p1.Name == p2.Name
            && p1.Price == p2.Price));
        }
        class ModelCompleteFakeRepositoryPricesUnder50 : IRepository
        {
            public IEnumerable<Product> Products { get; } = new Product[] {
 new Product { Name = "P1", Price = 5M },
 new Product { Name = "P2", Price = 48.95M },
 new Product { Name = "P3", Price = 19.50M },
 new Product { Name = "P3", Price = 34.95M }};
            public void AddProduct(Product p)
            {
                // do nothing - not required for test
            }
        }
        [Fact]
        public void IndexActionModelIsCompletePricesUnder50()
        {
            // Arrange
            var controller = new HomeController();
            controller.Repository = new ModelCompleteFakeRepositoryPricesUnder50();
            // Act
            var model = (controller.Index() as ViewResult)?.ViewData.Model
            as IEnumerable<Product>;
            // Assert
            Assert.Equal(controller.Repository.Products, model,
            Comparer.Get<Product>((p1, p2) => p1.Name == p2.Name
            && p1.Price == p2.Price));
        }
    }
}
我的存储库:

public class SimpleRepository : IRepository
    {
        private static SimpleRepository sharedRepository = new SimpleRepository();
        private Dictionary<string, Product> products = new Dictionary<string, Product>();

        public static SimpleRepository SharedRepository => sharedRepository;

        public SimpleRepository()
        {
            var initialItems = new[]
            {
                new Product {Name = "Kayak", Price = 275M},
                new Product { Name = "Lifejacket", Price = 48.95M },
                new Product { Name = "Soccer ball", Price = 19.50M },
                new Product { Name = "Corner flag", Price = 34.95M }
            };
            foreach(var p in initialItems)
            {
                AddProduct(p);
            }
            //products.Add("Error", null);
        }

        public IEnumerable<Product> Products => products.Values;

        public void AddProduct(Product p) => products.Add(p.Name, p);
    }

如果这看起来是一个愚蠢的问题,我很抱歉,但我刚刚开始研究单元测试。如果有人能向我解释问题所在,我将不胜感激。非常感谢那些花时间帮忙的人。

因为在您的
索引
方法中,您指的是
SimpleRepository
,而不是
存储库
成员

替换

public IActionResult Index() => View(SimpleRepository.SharedRepository.Products);

我还应该补充一点,您可能希望查看代码的结构,并将存储库注入构造函数中。另外,两个不同的测试测试相同的东西,所以只需要其中一个


编辑:我的答案解决了您当前的问题,@Nkosi answer显示了您应该如何正确地执行此操作。

因为在您的
索引
方法中,您指的是
SimpleRepository
,而不是
存储库
成员

替换

public IActionResult Index() => View(SimpleRepository.SharedRepository.Products);

我还应该补充一点,您可能希望查看代码的结构,并将存储库注入构造函数中。另外,两个不同的测试测试相同的东西,所以只需要其中一个


编辑:我的答案解决了您当前的问题,而@Nkosi answer显示了您应该如何正确地执行此操作。

我首先建议您使用

方法和类应该明确地要求(通常通过方法参数或构造函数参数)它们需要的任何协作对象才能正常工作

所以控制器最终会变成这样

public class HomeController : Controller {
    private readonly IRepository repository;

    public HomeController(IRepository repository) {
        this.repository = repository;
    }

    public IActionResult Index() => View(repository.Products.ToList());

    [HttpGet]
    public IActionResult AddProduct() => View(new Product());

    [HttpPost]
    public IActionResult AddProduct(Product p) {
        repository.AddProduct(p);
        return RedirectToAction("Index");
    }
}
以避免在独立单元测试期间访问共享存储库时最初犯下的错误,该错误导致您的断言失败

尽量避免将类与静态或共享依赖项紧密耦合。注入这种依赖的抽象会更安全

测试的简化版本现在可以清楚地执行如下操作

class ModelCompleteFakeRepository : IRepository {
    public IEnumerable<Product> Products { get; } = new Product[] {
        new Product { Name = "P1", Price = 275M },
        new Product { Name = "P2", Price = 48.95M },
        new Product { Name = "P3", Price = 19.50M },
        new Product { Name = "P3", Price = 34.95M }
    };

    public void AddProduct(Product p) {
        // do nothing - not required for test
    }
}

[Fact]
public void IndexActionModelIsComplete() {
    // Arrange
    var repository = new ModelCompleteFakeRepository();
    var controller = new HomeController(repository);
    var expected = repository.Products;

    // Act
    var actual = (controller.Index() as ViewResult)?.ViewData.Model as IEnumerable<Product>;

    // Assert
    Assert.IsNotNull(actual);
    Assert.Equal(expected, actual);
}
class ModelCompleteMakeRepository:IRepository{
公共IEnumerable产品{get;}=新产品[]{
新产品{Name=“P1”,价格=275M},
新产品{Name=“P2”,价格=4895M},
新产品{Name=“P3”,价格=19.50M},
新产品{Name=“P3”,价格=3495万}
};
公共产品(产品p){
//不做任何事情-测试不需要
}
}
[事实]
public void IndexActionModelIsComplete(){
//安排
var repository=new ModelCompleteFakeRepository();
var控制器=新的HomeController(存储库);
var预期=repository.Products;
//表演
var actual=(controller.Index()作为ViewResult)?.ViewData.Model作为IEnumerable;
//断言
Assert.IsNotNull(实际值);
断言。相等(预期、实际);
}

我首先建议您使用

方法和类应该明确地要求(通常通过方法参数或构造函数参数)它们需要的任何协作对象才能正常工作

所以控制器最终会变成这样

public class HomeController : Controller {
    private readonly IRepository repository;

    public HomeController(IRepository repository) {
        this.repository = repository;
    }

    public IActionResult Index() => View(repository.Products.ToList());

    [HttpGet]
    public IActionResult AddProduct() => View(new Product());

    [HttpPost]
    public IActionResult AddProduct(Product p) {
        repository.AddProduct(p);
        return RedirectToAction("Index");
    }
}
以避免在独立单元测试期间访问共享存储库时最初犯下的错误,该错误导致您的断言失败

尽量避免将类与静态或共享依赖项紧密耦合。注入这种依赖的抽象会更安全

测试的简化版本现在可以清楚地执行如下操作

class ModelCompleteFakeRepository : IRepository {
    public IEnumerable<Product> Products { get; } = new Product[] {
        new Product { Name = "P1", Price = 275M },
        new Product { Name = "P2", Price = 48.95M },
        new Product { Name = "P3", Price = 19.50M },
        new Product { Name = "P3", Price = 34.95M }
    };

    public void AddProduct(Product p) {
        // do nothing - not required for test
    }
}

[Fact]
public void IndexActionModelIsComplete() {
    // Arrange
    var repository = new ModelCompleteFakeRepository();
    var controller = new HomeController(repository);
    var expected = repository.Products;

    // Act
    var actual = (controller.Index() as ViewResult)?.ViewData.Model as IEnumerable<Product>;

    // Assert
    Assert.IsNotNull(actual);
    Assert.Equal(expected, actual);
}
class ModelCompleteMakeRepository:IRepository{
公共IEnumerable产品{get;}=新产品[]{
新产品{Name=“P1”,价格=275M},
新产品{Name=“P2”,价格=4895M},
新产品{Name=“P3”,价格=19.50M},
新产品{Name=“P3”,价格=3495万}
};
公共产品(产品p){
//不做任何事情-测试不需要
}
}
[事实]
public void IndexActionModelIsComplete(){
//安排
var repository=new ModelCompleteFakeRepository();
var控制器=新的HomeController(存储库);
var预期=repository.Products;
//表演
var actual=(controller.Index()作为ViewResult)?.ViewData.Model作为IEnumerable;
//断言
Assert.IsNotNull(实际值);
断言。相等(预期、实际);
}

显示正在测试的方法。您试图测试的预期行为是什么?我已经更新了我的问题,显示了正在测试的方法。你想要测试的行为是什么?我已经更新了我的问题,非常感谢。你让我想了很多,非常感谢。你让我想了很多。
public IActionResult Index() => View(Repository.Products);
public class HomeController : Controller {
    private readonly IRepository repository;

    public HomeController(IRepository repository) {
        this.repository = repository;
    }

    public IActionResult Index() => View(repository.Products.ToList());

    [HttpGet]
    public IActionResult AddProduct() => View(new Product());

    [HttpPost]
    public IActionResult AddProduct(Product p) {
        repository.AddProduct(p);
        return RedirectToAction("Index");
    }
}
class ModelCompleteFakeRepository : IRepository {
    public IEnumerable<Product> Products { get; } = new Product[] {
        new Product { Name = "P1", Price = 275M },
        new Product { Name = "P2", Price = 48.95M },
        new Product { Name = "P3", Price = 19.50M },
        new Product { Name = "P3", Price = 34.95M }
    };

    public void AddProduct(Product p) {
        // do nothing - not required for test
    }
}

[Fact]
public void IndexActionModelIsComplete() {
    // Arrange
    var repository = new ModelCompleteFakeRepository();
    var controller = new HomeController(repository);
    var expected = repository.Products;

    // Act
    var actual = (controller.Index() as ViewResult)?.ViewData.Model as IEnumerable<Product>;

    // Assert
    Assert.IsNotNull(actual);
    Assert.Equal(expected, actual);
}