【问题标题】:Mstest Controller and Service Layer With Dependency Injection (Autofac) and Automapper具有依赖注入 (Autofac) 和 Automapper 的 Mstest 控制器和服务层
【发布时间】:2012-11-22 15:17:21
【问题描述】:

我正在尝试将测试层带入我的项目,但我没有到达那里:(希望有人可以帮助我。

Controller(基于 Automapper 映射和依赖注入容器):

    public virtual ActionResult SearchCategories(string keywords)
    {
        var result = _categoryService.SearchCategories(keywords);

        var resultViewModel = Mapper.
            Map<IList<SearchCategoriesDto>, 
                IList<SearchCategoriesViewModel>>(result);

        return View(resultViewModel);
    }    

服务层:

   public IList<SearchCategoriesDto> SearchCategories(String keywords)
    {
        // Find the keywords in the Keywords table
        var keywordQuery = _keywordRepository.Query;

        foreach (string keyword in splitKeywords)
        {
            keywordQuery = keywordQuery.Where(p => p.Name == keyword);
        }

        // Get the Categories from the Search
        var keywordAdCategoryQuery = _keywordAdCategoryRepository.Query;
        var categoryQuery = _categoryRepository.Query;

        var query = from k in keywordQuery
                    join kac in keywordAdCategoryQuery on k.Id equals kac.Keyword_Id
                    join c in categoryQuery on kac.Category_Id equals c.Id
                    select new SearchCategoriesDto
                    {
                        Id = c.Id,
                        Name = c.Name,
                        SearchCount = keywordAdCategoryQuery
                             .Where(s => s.Category_Id == c.Id)
                             .GroupBy(p => p.Ad_Id).Count(),
                        ListController = c.ListController,
                        ListAction = c.ListAction
                    };

        var searchResults = query.Distinct().ToList();

        return searchResults;
    }

测试完成但不工作:

    [TestMethod]
    public void Home_SearchCategories_Test()
    {
        // Setup
        var catetoryService = new CategoryService(
                                   _categoryRepository, 
                                   _keywordRepository, 
                                   _keywordAdCategoryRepository);

        // Act
        var result = catetoryService.SearchCategories("audi");

        // Add verifications here
        Assert.IsTrue(result.Count > 0);
    }

谢谢。

【问题讨论】:

  • 您是遇到错误还是测试失败了?
  • 另外,您是否为您的服务所依赖的这 3 个存储库提供模拟?
  • @Daniel,嗨,我想测试与数据库的集成。我没有收到错误,我只是没有获得所需的代码。
  • 我不确定我是否理解这个问题。你不去那里是什么意思?在 Visual Studio 中运行该测试时会发生什么?
  • @Daniel,我无法创建 _categoryRepository、_keywordRepository、_keywordAdCategoryRepository 上下文。

标签: asp.net-mvc-3 unit-testing mstest automapper autofac


【解决方案1】:

我假设您想使用真实的存储库和数据库为您的类别服务创建一个集成测试。 (与单元测试相反,您将在这些存储库中使用 stub\mocks 并单独测试服务类)

因此,您将拥有一个分离的测试程序集,您将在其中添加集成测试,例如在服务级别为集成测试提供一个类。 在该类上,您将在运行每个测试之前创建所有存储库和 CategoryService 的实例,在具有属性 [TestInitialize] 的方法上。 (具有此属性的方法将在每次测试之前由 msTest 运行)

由于您还真正地使用数据库,因此您希望确保所使用的所有资源都已处理完毕。例如,处理实体框架上下文。在这种情况下,您将添加一个具有 [TestCleanup] 属性的方法,您将在其中执行所需的任何清理逻辑。 (具有此属性的方法将在每次测试后由 msTest 运行)

使用这些方法,您将确保每次测试都有新的服务和存储库实例。然后,您将按照 AAA 模式(Arrange、Act、Assert)实施每个单独的集成测试

因此,带有单个测试的集成测试类示例可能如下所示:

public class CategoryServiceIntegrationTest
{
    //dependencies of your class under test
    private ICategoryRepository _categoryRepository; 
    private IKeywordRepository _keywordRepository;
    private IKeywordAdCategoryRepository _keywordAdCategoryRepository;

    //your class under test
    private CategoryService _categoryService;

    [TestInitialize]
    public void InitializeBeforeRunningATest()
    {
        //manually create instances of the classes implementing the repositories
        //I don´t know about how they are implemented but I guess
        //you would need to provide the name of a connection string in the config file (in that case this should be in a config file of the test project)
        //,the connection string itself
        //or maybe you need to initialize an entity framework context
        _categoryRepository = new CategoryRepository( /*whatever you need to provide*/);
        _keywordRepository = new KeywordRepository( /*whatever you need to provide*/);
        _keywordAdCategoryRepository = new KeywordAdCategoryRepository( /*whatever you need to provide*/);

        //Create the class under test with all repositories dependencies          
        //as it is an integration test, they are your real objects and not mocks\stubs
        _categoryService = new CategoryService(_categoryRepository, 
                                               _keywordRepository,
                                               _keywordAdCategoryRepository);
    }

    [TestCleanup]
    public void CleanDatabaseResources()
    {
        //just in case you need to do something like disposing an EF context object          
    }

    [TestMethod]
    public void Home_SearchCategories_Test()
    {
        // Arrange
        var keywords = "audi";

        // Act (the _categoryService instance was created in the initialize method)
        var result = _categoryService.SearchCategories(keywords);

        // Assert
        Assert.IsTrue(result.Count > 0);
    }
}

【讨论】:

  • 非常感谢您的回答,但我无法通过测试。我使用 Autofac 作为 ICategoryService 和 CategoryService 的 IoC 容器,并且我使用了一个由接口 IRepository 访问的唯一存储库。关于这一切,我不知道如何为方法“SearchCategories”测试创建存储库。我也试着把“AutofacConfig.RegisterDependencies();”在 TestInitializes 方法中获取所有构造函数,但没有成功:(
  • 所以你的类 CategoryService 依赖于诸如 IRepository 之类的东西。但是您会配置自动映射器,以便可以将这些接口解析为具体的类。您可以在测试中以相同的方式手动创建它们。另一种选择就是您所说的,在您的测试类中包含并配置 Autofac 并使用它来解析类别服务。
  • 嗨,再次感谢,我终于到了那里,我会发布结果。再次感谢您的时间和帮助;)
【解决方案2】:

为服务(在本例中为类别服务)构建集成测试的解决方案,使用 Autofac、Automapper(在此服务中不是必需的,但如果有必要,您需要尽可能放入 TestInitialize 方法请参阅以下解决方案的注释行)和 Daniel J.G. 的实体框架。帮助(感谢丹尼尔):

首先,我使用 MSTest 创建了一个单独的测试项目(只是因为有很多关于它的文档)。

其次你需要把实体框架的连接字符串放在测试数据所在的地方:

<connectionStrings>
    <add name="DB" connectionString="Data Source=.\sqlexpress;Database=DBNAME;UID=DBUSER;pwd=DBPASSWORD;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
</connectionStrings>

之后的部分

第三次为测试创建类:

namespace Heelp.Tests
{
    [TestClass]
    public class CategoryServiceIntegrationTest
    {
        // Respositories dependencies
        private IRepository<Category> _categoryRepository;
        private IRepository<Keyword> _keywordRepository;
        private IRepository<KeywordAdCategory> _keywordAdCategoryRepository;

        // Service under test: Category Service
        private CategoryService _categoryService;

        // Context under test: HeelpDB Connection String in app.config
        private HeelpDbContext db;

        [TestInitialize]
        public void InitializeBeforeRunningATest()
        {
            // IoC dependencies registrations
            AutofacConfig.RegisterDependencies();

            // HERE YOU CAN CALL THE AUTOMAPPER CONFIGURE METHOD
            // IN MY PROJECT I USE AutoMapperConfiguration.Configure();  
            // IT'S LOCATED IN THE App_Start FOLDER IN THE AutoMapperConfig.cs CLASS
            // CALLED FROM GLOBAL.ASAX Application_Start() METHOD

            // Database context initialization
            db = new HeelpDbContext();

            // Repositories initialization
            _categoryRepository = new Repository<Category>(db);
            _keywordRepository = new Repository<Keyword>(db);
            _keywordAdCategoryRepository = new Repository<KeywordAdCategory>(db); 

            // Service initialization
            _categoryService = new CategoryService(_categoryRepository,
                                                   _keywordRepository,
                                                   _keywordAdCategoryRepository);
        }

        [TestCleanup]
        public void CleanDatabaseResources()
        {
            // Release the Entity Framework Context for other tests that will create a fresh new context.
            // With this method, we will make sure that we have a fresh service and repositories instances on each test. 
            db.Dispose();
        }

        [TestMethod]
        public void Home_SearchCategories_Test()
        {
            // Arrange
            var keywords = "audi";

            // Act (the _categoryService instance was created in the initialize method)
            var result = _categoryService.SearchCategories(keywords);

            // Assert
            Assert.IsTrue(result.Count > 0);
        }
    }

}

现在你只需要运行测试看看它是否通过了。

为了保证集成测试,我建议第二个数据库与原始/生产数据库在表格方面相同,但只有您的测试数据。

这将确保测试结果根据您的测试数据保持不变。

唯一的缺点是您需要保持表的sincronized,但您可以使用Simego 的SQL Admin Studio Freeware Tool 来实现。

问候。

【讨论】:

    猜你喜欢
    • 2011-05-11
    • 2014-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多