65.9K
CodeProject 正在变化。 阅读更多。
Home

使用模型驱动的方法增强测试驱动开发

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (14投票s)

2014 年 7 月 8 日

CPOL

14分钟阅读

viewsIcon

32181

downloadIcon

226

使用 Mo+ 增强测试驱动方法来开发应用程序。

引言

测试驱动开发(TDD)是一种流行且有效的软件开发方法。TDD 的一些优点包括能够:

  • 在编写代码之前更好地理解应该编写的代码
  • 以成功或失败的方式快速衡量进度
  • 通过有条不紊、更短、可衡量的步骤来分而治之,解决大型复杂问题
  • 快速识别对正在开发的系统进行更改所带来的影响

在开发过程中的任何时间点以任何形式创建的模型通常都是有益的。借助模型,您能够:

  • 在宏观层面上更好地理解和沟通系统应该做什么
  • 通过清晰的数据、关系和工作流程来分而治之,解决大型复杂问题
  • 在开发代码时利用模型信息

在本文中,我们将通过一个示例问题,该问题利用了 TDD,同时也融入了面向模型的方法,从而结合了 TDD 和使用模型的优点。

请原谅本示例中使用的代码。这些代码过于简单,示例也是刻意设计的。本文的重点不在于代码质量,而在于这种方法以及您如何将该方法应用于自己的项目。

我希望本文能引发一些讨论,这些讨论可用于进一步完善本文中概述的方法和示例。我没有在许多实际项目中直接应用 TDD 的深厚背景,非常欢迎来自 TDD 社区的意见。

未来的文章主题可能会涵盖行为驱动开发(BDD)。

背景

Mo+ 将是本文中融入面向模型方法的选择,因为 Mo+ 是专门为此类方法设计的。 Mo+ 面向模型的编程语言和用于面向模型开发的 Mo+ Solution Builder IDE 在这篇 Code Project 文章中有所介绍。 本文旨在演示如何将面向模型的开发应用于 TDD。 文中将展示 Mo+ 模型和模板,但本文并非 Mo+ 语言或工具的使用指南 不过,您可以在不同程度上将本文概述的技术与其他建模和代码生成工具结合使用。如果您正在尝试使用 Mo+,上述文章中还有一个“了解更多”部分,提供了可帮助您入门的附加信息。

该示例将使用 C# 实现代码,并使用 Visual Studio 单元测试进行测试。

本文的一些灵感来源包括 Kent Beck 的书Scott Ambler 的博客

流程定义

测试驱动开发(TDD)可以用这个基本公式来描述:

TDD = TFD + 重构

其中,您将测试先行开发(TFD)与重构代码的步骤相结合,重构后的代码仍能通过所有测试。

模型驱动开发(MDD)通常意味着您用一种或多种形式的模型创建前端架构,并将(通常更复杂的)模型转换为代码。我们在这里不打算这样做。所以,我想创造一个术语,即面向模型的开发(MOD),它允许在开发过程的任何时候创建和使用一个简单、专注的模型用于开发目的。使用 MOD,您可以自由地、只在需要时、尽可能少或多地使用这个专注的模型进行开发。

最后,我将再创造一个术语,即面向模型的测试驱动开发(MOTDD),这将是我们在这里要使用的流程。MOTDD 可以用这个公式来描述:

MOTDD = TDD + MOD

通过 MOTDD,除了按照标准 TDD 重构代码外,您还会考虑以面向模型的方式重构您的测试用例和代码。请看下图,其中上半部分代表 TDD,下半部分代表融入 MOD 的过程:

这个过程将在下面的示例中进一步解释,但您现在可能已经有几个问题了。我为什么要将 MOD 应用于 TDD?我应该在什么时候将 MOD 应用于 TDD?

为何应用 MOD?

当您可以使用 MOD 来增强您在 TDD 中已经实践的重构目标和步骤时,应用 MOD 是有用的。当您能够比仅使用自定义方法更快、更安全地实现目标时,MOD 就很有用。

何时应用 MOD?

当模型中的信息变得已知且可用时,以及当您识别出可以减少自定义代码量、并使您的系统基于模型和团队最佳实践而对变化更具鲁棒性的面向模型的模式时,您就可以应用 MOD。

应用 MOD 的目标不是生成尽可能多的面向模型的代码,而是实现您在重构时考虑的总体目标。您可能会在重构中发现机会,用健壮的、与模型无关的自定义代码来替换面向模型的代码中的重复部分。而且,这是一件好事!

示例

我们想要实现一个简单的电子商务场景,仅将 Northwind 数据库作为模型信息的来源。 尽管模型可以包含更多的信息,但在本例中,我们只会利用实体和基本属性来生成面向模型的代码。

考虑一个变更:我们希望这个系统做什么?让我们从一些简单的用户故事开始:

  • 客户注册
  • 客户查找产品
  • 客户订购产品
  • 客户收到产品

以测试先行的方式开始

编写自定义测试:我们知道我们的系统与产品、订单和客户有关。为了开始,让我们将上述用户故事转化为测试并让它们通过(为简洁起见,我们在这里一次性完成)。我们的客户测试如下:

    [TestClass]
    public class CustomerTests
    {
        [TestMethod]
        public void TestRegisterCustomer()
        {
            Customer customer = Customer.RegisterCustomer("John Doe", "555-1212");
            Assert.AreEqual(customer.ContactName, "John Doe");
            Assert.AreEqual(customer.Phone, "555-1212");
        }
        [TestMethod]
        public void TestFindProduct()
        {
            Product product = Customer.FindProduct("My Widget");
            Assert.AreEqual(product.ProductName, "My Widget");
        }
        [TestMethod]
        public void TestOrderProduct()
        {
            Order order = Customer.OrderProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }
        [TestMethod]
        public void TestGetProduct()
        {
            Order order = Customer.GetProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }
    } 

当然,这些测试无法编译或运行。

进行自定义代码更改: 让我们编写一些最少的代码来使这些测试通过。首先,我们的 ProductOrderCustomer 模型类如下所示:

    public partial class Product
    {
        public string ProductName { get; set; }
    }

    public partial class Order
    {
        public string ProductName { get; set; }
    }

    public partial class Customer
    {
        public string ContactName { get; set; }

        public string Phone { get; set; }

        public static Customer RegisterCustomer(string contactName, string phone)
        {
            Customer customer = new Customer { ContactName = contactName, Phone = phone };
            return customer;
        }

        public static Product FindProduct(string productName)
        {
            Product product = new Product { ProductName = productName };
            return product;
        }

        public static Order OrderProduct(string productName)
        {
            Order order = new Order { ProductName = productName };
            return order;
        }

        public static Order GetProduct(string productName)
        {
            Order order = new Order { ProductName = productName };
            return order;
        }
    } 

好的,我们的单元测试通过了。 请参阅示例下载中的步骤 1 代码。

自定义重构

进行自定义代码更改:我们审视为了让测试通过而编写的几乎是伪代码的代码,意识到我们需要以某种方式存储客户、产品和订单数据。所以,让我们创建一个伪仓库(repository),用来存储这些数据的列表:

    public partial class Repository
    {
        public static List<Product> Products { get; set; }

        public static List<Customer> Customers { get; set; }

        public static List<Order> Orders { get; set; }

    }

重构自定义代码: 让我们在客户类的方法中使用这个“仓库”。我们看到 OrderProduct() GetProduct() 中存在一些潜在问题需要稍后解决,但至少我们可以修改 RegisterCustomer()FindProduct() 来使用这个仓库:

    public partial class Customer
    {
        public string ContactName { get; set; }

        public string Phone { get; set; }

        public static Customer RegisterCustomer(string contactName, string phone)
        {
            Customer customer = new Customer { ContactName = contactName, Phone = phone };
            Repository.Customers.Add(customer);
            return customer;
        }

        public static Product FindProduct(string productName)
        {
            Product product = Repository.Products.FirstOrDefault(i => i.ProductName == productName);
            return product;
        }

        public static Order OrderProduct(string productName)
        {
            Order order = new Order { ProductName = productName };
            return order;
        }

        public static Order GetProduct(string productName)
        {
            Order order = new Order { ProductName = productName };
            return order;
        }
    }

我们的测试立即因仓库中的空引用错误而失败。

进行自定义代码更改: 我们需要改变测试的运行方式才能让它们正常工作。 所以,我们将添加一个 Setup() 方法,该方法在每个测试运行前被调用,用一个产品和一个客户来初始化仓库:

    [TestClass]
    public class CustomerTests
    {
        [TestInitialize]
        public void Setup()
        {
            Repository.Products = new List<Product>();
            Repository.Products.Add(new Product { ProductName = "My Widget" });
            Repository.Customers = new List<Customer>();
            Repository.Customers.Add(new Customer { ContactName = "Jane Doe", Phone = "555-1213" });
        }

        [TestMethod]
        public void TestRegisterCustomer()
        {
            Customer customer = Customer.RegisterCustomer("John Doe", "555-1212");
            Assert.AreEqual(customer.ContactName, "John Doe");
            Assert.AreEqual(customer.Phone, "555-1212");
        }

        [TestMethod]
        public void TestFindProduct()
        {
            Product product = Customer.FindProduct("My Widget");
            Assert.AreEqual(product.ProductName, "My Widget");
        }

        [TestMethod]
        public void TestOrderProduct()
        {
            Order order = Customer.OrderProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }

        [TestMethod]
        public void TestGetProduct()
        {
            Order order = Customer.GetProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }
    }

好的,现在我们的测试可以运行了!请参阅示例下载中的步骤 2 代码。 

一些简单的面向模型的重构 

以 Northwind(SQL Server 或 MySQL)数据库为模型源,我们审视模型以及其中的信息如何影响我们的整体设计。 下图展示了 Mo+ 中我们示例感兴趣的实体和属性的一些模型信息: 

我们立即发现我们的 CustomerProductOrder 模型类中缺少属性,并且需要一个订单结构来允许一个订单中有多个产品。 但是,别着急! 我们正在采用测试驱动的方法,我们不打算现在就尝试整合所有这些信息。

重构面向模型的代码:我们确实注意到,我们可以轻松地以面向模型的方式重构我们的 Repository 类,使其对未来模型的变化具有鲁棒性。 我很懒,我想我可以先不写一个面向模型的测试,因为经过面向模型的重构后的代码应该与之前几乎相同。

重构面向模型的代码涉及创建或修改一个(或多个)代码模板,并生成更新的面向模型的代码。 下面是 Repository 类主体部分的 Mo+ 代码模板(为便于阅读,Mo+ 模板代码将仅以图片形式显示,完整模板的实际代码可在附加的示例下载中找到)。 在第 16 行,foreach 语句遍历解决方案中我们感兴趣的、要添加到仓库的每个 Entity,在第 18 行,我们正在为特定的实体创建一个仓库,并从模型中插入 EntityName 信息。 在我们的模型中,我们用“ForDev”标记了感兴趣的实体,这样我们就可以将仓库限制在当前感兴趣的实体上:

我们生成的 Repository 类如下所示(与之前几乎完全相同): 

	public partial class Repository
	{
		public static List<Customer> Customers  { get; set; }
		
		public static List<Order> Orders  { get; set; }
		
		public static List<Product> Products  { get; set; }
		
	}
我们的单元测试仍然通过。  请参阅示例下载中的步骤 3 代码。 请注意,示例中生成的文件以“_G”命名,以便快速识别。 此步骤的项目级 Mo+ 代码模板名为 RepositoryCode

向处理订单更进一步 

在考虑这个小小的电子商务场景时,我们意识到需要解决几个问题: 

  • 客户通常会稍后登录下单
  • 已登录的客户下订单并检索订单信息

考虑一个变更:所以,我们再添加一个用户故事

  • 客户登录

编写自定义测试:我们为这个用户故事创建一个单元测试,并更新我们的订购产品和获取产品的测试,使其基于已登录的客户

    [TestClass]
    public class CustomerTests
    {
        [TestInitialize]
        public void Setup()
        {
            Repository.Products = new List<Product>();
            Repository.Products.Add(new Product { ProductName = "My Widget" });
            Repository.Customers = new List<Customer>();
            Repository.Customers.Add(new Customer { ContactName = "Jane Doe", Phone = "555-1213" });
        }

        [TestMethod]
        public void TestRegisterCustomer()
        {
            Customer customer = Customer.RegisterCustomer("John Doe", "555-1212");
            Assert.AreEqual(customer.ContactName, "John Doe");
            Assert.AreEqual(customer.Phone, "555-1212");
        }

        [TestMethod]
        public void TestLoginCustomer()
        {
            Customer customer = Customer.FindCustomer("Jane Doe");
            Assert.AreEqual(customer.ContactName, "Jane Doe");
            Assert.AreEqual(customer.Phone, "555-1213");
        }

        [TestMethod]
        public void TestFindProduct()
        {
            Product product = Product.FindProduct("My Widget");
            Assert.AreEqual(product.ProductName, "My Widget");
        }

        [TestMethod]
        public void TestOrderProduct()
        {
            Customer customer = Customer.FindCustomer("Jane Doe");
            Order order = customer.OrderProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }

        [TestMethod]
        public void TestGetProduct()
        {
            Customer customer = Customer.FindCustomer("Jane Doe");
            Order order = customer.OrderProduct("My Widget");
            order = customer.GetProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }
    }

进行自定义代码更改:我们进行自定义更改,将产品搜索移至 Product 类,并在 Customer 类中添加一个订单列表和支持客户登录和订单的方法

    public partial class Product
    {
        public string ProductName { get; set; }

        public static Product FindProduct(string productName)
        {
            Product product = Repository.Products.FirstOrDefault(i => i.ProductName == productName);
            return product;
        }
    }

    public partial class Customer
    {
        public string ContactName { get; set; }

        public string Phone { get; set; }

        public List<Order> Orders { get; set; }

        public static Customer RegisterCustomer(string contactName, string phone)
        {
            Customer customer = new Customer { ContactName = contactName, Phone = phone };
            Repository.Customers.Add(customer);
            return customer;
        }

        public static Customer FindCustomer(string contactName)
        {
            Customer customer = Repository.Customers.FirstOrDefault(i => i.ContactName == contactName);
            return customer;
        }

        public Order OrderProduct(string productName)
        {
            if (Orders == null) Orders = new List<Order>();

            Order order = new Order { ProductName = productName };
            Orders.Add(order);

            return order;
        }

        public Order GetProduct(string productName)
        {
            if (Orders == null) return null;

            foreach (Order order in Orders)
            {
                if (order.ProductName == productName)
                    return order;
            }

            return null;
        }
    }
我们的单元测试仍然通过。  请参阅示例下载中的步骤 4 代码。

融入模型结构

现在我们想要利用模型中找到的关于客户、产品和订单的一些整体信息。 将大量面向模型的代码与通过测试驱动方法创建的自定义代码相结合,可能看起来令人生畏。 但是,就像我们之前的步骤一样,我们从一些单元测试开始,并让它们通过。

编写自定义测试:让我们从一个简单的自定义单元测试开始。 我们知道我们想把所有信息都保存在仓库中,对于每种类型的对象,我们需要能够将它们添加到仓库中。 所以,让我们从这个 ProductCRUDTests 类开始,测试向仓库添加一个 Product

    [TestClass]
    public partial class ProductCRUDTests
    {
        [TestInitialize]
        public void Setup()
        {
            Repository.Products = new List<Product>();
        }

        [TestMethod]
        public void AddProduct()
        {
            int count = Repository.Products.Count;
            Product.AddProduct(new Product());
            Assert.AreEqual(count + 1, Repository.Products.Count);
        }

    }

当然,这个单元测试会失败,因为我们没有 AddProduct() 方法。

进行自定义代码更改: 那么,让我们把这个缺失的方法添加到 Product 类中

    public partial class Product
    {
        public string ProductName { get; set; }

        public static Product FindProduct(string productName)
        {
            Product product = Repository.Products.FirstOrDefault(i => i.ProductName == productName);
            return product;
        }

        public static void AddProduct(Product product)
        {
            Repository.Products.Add(product);
        }
    }

单元测试再次通过。

编写面向模型的测试:编写面向模型的测试涉及为一个测试创建或修改一个(或多个)代码模板,并生成更新的面向模型的测试代码。 首先,我们利用我们的 ProductCRUDTests 类作为模板的起点,为模型中的每个实体添加测试。 测试类主体的 Mo+ 模板如下所示(我们在其中插入来自模型的 EntityName 信息)

接下来,我们用这个模板为模型中的每个实体生成额外的单元测试。 ProductCRUDTests 类的主体应该与我们刚刚创建的自定义类完全相同。 例如,OrderCRUDTests 类如下所示

    [TestClass]
    public partial class OrderCRUDTests
    {
        [TestInitialize]
        public void Setup()
        {
            Repository.Orders = new List<Order>();
        }

        [TestMethod]
        public void AddOrder()
        {
            int count = Repository.Orders.Count;
            Order.AddOrder(new Order());
            Assert.AreEqual(count + 1, Repository.Orders.Count);
        }

    }

现在我们可以删除自定义的 ProductCRUDTests 文件,因为它不再需要了。 但是,当然,像 OrderCRUDTests 这样的额外单元测试会失败,因为我们相应的模型类没有 add 方法。 所以,是时候以面向模型的方式重构我们的模型类了!

重构面向模型的代码:现在我们想对我们的模型类进行一些面向模型的重构。 为了重构我们的模型类,我们以 Product 类作为模板的起点。 我们希望生成的模型类包含模型属性和 add 方法。 模型类主体的 Mo+ 模板如下所示(其中我们在第 11 行添加每个 Property 的信息,并从模型中插入 PropertyName EntityName 信息)

接下来,我们用这个模板为模型中的每个实体生成模型类代码。 Product 类的生成代码如下所示

    public partial class Product
    {
        
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public int? SupplierID { get; set; }
        public int? CategoryID { get; set; }
        public string QuantityPerUnit { get; set; }
        public decimal? UnitPrice { get; set; }
        public short? UnitsInStock { get; set; }
        public short? UnitsOnOrder { get; set; }
        public short? ReorderLevel { get; set; }
        public bool Discontinued { get; set; }

        public static void AddProduct(Product product)
        {
            Repository.Products.Add(product);
        }
    }

哦,但是现在我们的代码无法编译了! 一些生成的属性和方法也存在于我们的自定义代码中。

我们需要移除自定义代码中的重复元素。 Product 类的自定义代码如下所示

        public static Product FindProduct(string productName)
        {
            Product product = Repository.Products.FirstOrDefault(i => i.ProductName == productName);
            return product;
        }

好的,现在一切都能编译了,而且单元测试也通过了!   请参阅示例下载中的步骤 5 代码。  用于 CRUD 单元测试和模型代码的实体级 Mo+ 代码模板分别名为 CRUDTestCode ModelClassCode

利用模型结构处理订单 

现在我们已经融入了实际的模型结构,我们可以重新审视我们处理订单的方式。 但首先,让我们做一些自定义更改,以利用我们新生成的方法向仓库添加项目。 我们替换了 CustomerTests.Setup()Customer.RegisterCustomer() 中直接调用向仓库添加项目的代码(您可以在示例下载中看到更改)。 我们再次运行单元测试,它们通过了。

现在,让我们根据我们现在对模型结构的了解,来看看我们对订单的支持。 下订单的单元测试是 CustomerTests.TestOrderProduct()

        [TestMethod]
        public void TestOrderProduct()
        {
            Customer customer = Customer.FindCustomer("Jane Doe");
            Order order = customer.OrderProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }

编写(修改)自定义测试:我们知道一个订单不应该有产品名称,而应该有一组包含产品相关信息的订单详情。 如果客户订购一个产品,这个单元测试应该看起来像这样

        [TestMethod]
        public void TestOrderProduct()
        {
            Customer customer = Customer.FindCustomer("Jane Doe");
            Order order = customer.OrderProduct("My Widget");
            Assert.AreEqual(1, order.OrderDetails.Count);
            Product product = Product.FindProduct(order.OrderDetails[0].ProductID);
            Assert.IsNotNull(product);
            Assert.AreEqual(product.ProductName, "My Widget");
        }

此更改会导致编译错误,我们需要先修复这些错误。

进行自定义代码更改:我们需要将 OrderDetails 列表添加到自定义的 Order 类中

    public partial class Order
    {
        public string ProductName { get; set; }

        public List<OrderDetail> OrderDetails { get; set; }
    }

进行自定义代码更改: 我们需要在自定义的 Product 类中添加另一个 FindProduct() 方法

        public static Product FindProduct(int productID)
        {
            Product product = Repository.Products.FirstOrDefault(i => i.ProductID == productID);
            return product;
        }

好的,这可以编译,但是 TestOrderProduct() 测试失败了。 我们在 Customer.OrderProduct() 中看到了一些问题

        public Order OrderProduct(string productName)
        {
            if (Orders == null) Orders = new List<Order>();

            Order order = new Order { ProductName = productName };
            Orders.Add(order);

            return order;
        }

进行自定义代码更改: 订购产品应该向订单中添加一个 OrderDetail,其中包含在产品仓库中找到的产品信息

        public Order OrderProduct(string productName)
        {
            if (Orders == null) Orders = new List<Order>();

            Product product = Product.FindProduct(productName);
            if (product != null)
            {
                Order order = new Order { CustomerID = CustomerID };
                order.OrderDetails = new List<OrderDetail>();
                Orders.Add(order);
                Order.AddOrder(order);
                OrderDetail detail = new OrderDetail { ProductID = product.ProductID, OrderID = order.OrderID };
                order.OrderDetails.Add(detail);
                OrderDetail.AddOrderDetail(detail);

                return order;
            }

            return null;
        }

好的,现在 TestOrderProduct() 测试成功了,但现在我们破坏了 TestGetProduct()

        [TestMethod]
        public void TestGetProduct()
        {
            Customer customer = Customer.FindCustomer("Jane Doe");
            Order order = customer.OrderProduct("My Widget");
            order = customer.GetProduct("My Widget");
            Assert.AreEqual(order.ProductName, "My Widget");
        }

编写(修改)自定义测试: 我们发现测试中有几处错误,因为我们知道客户应该只能“获取”属于该客户有效订单一部分的产品。 所以这个单元测试应该更像这样

        [TestMethod]
        public void TestGetProduct()
        {
            Customer customer = Customer.FindCustomer("Jane Doe");
            Order order = customer.OrderProduct("My Widget");
            Product product = customer.GetProduct("My Widget");
            Assert.AreEqual(product.ProductName, "My Widget");
        }

进行自定义代码更改: 这当然会导致编译错误,我们将 Customer.GetProduct() 重构为一种返回任何订单中匹配产品的方法

        public Product GetProduct(string productName)
        {
            if (Orders == null) return null;

            foreach (Order order in Orders)
            {
                foreach (OrderDetail detail in order.OrderDetails)
                {
                    Product product = Product.FindProduct(detail.ProductID);
                    if (product != null && product.ProductName == productName)
                    {
                        return product;
                    }
                }
            }

            return null;
        }

此时,您也可以从 Order 中移除 ProductName 属性。 单元测试再次通过,我们有了一个看起来几乎像订单处理流程的东西(尽管我不会把我的钱投进去)。   请参阅示例下载中的步骤 6 代码。

继续迭代直到您满意为止

当然,这个例子离一个真正的电子商务系统还很远。 在实践中,您将继续迭代编写测试以覆盖您的用户故事,编写自定义代码使它们通过,重构自定义代码,以及重构面向模型的测试和代码。

我们将在这里再进行一次迭代。

自定义面向模型的测试:让我们增强我们的 AddProduct() 测试,使其能添加多个产品并验证 ID 是否唯一(顺便将方法重命名为 TestAddProduct()

        [TestMethod]
        public void TestAddProduct()
        {
            int count = Repository.Products.Count;
            Product product1 = new Product();
            Product.AddProduct(product1);
            Assert.AreEqual(count + 1, Repository.Products.Count);

            count = Repository.Products.Count;
            Product product2 = new Product();
            Product.AddProduct(product2);
            Assert.AreEqual(count + 1, Repository.Products.Count);

            Assert.AreNotEqual(product1.ProductID, product2.ProductID);
        }
这个测试失败了! 两个产品的 ID 相同。
 
自定义面向模型的代码:让我们在 Product.AddProduct() 中实现一些递增 ID 的功能
 
        public static void AddProduct(Product product)
        {
            product.ProductID = Repository.Products.Count + 1;
            Repository.Products.Add(product);
        }

好的,单元测试又通过了,呼!

重构面向模型的代码: 但是我们自定义了一些面向模型的代码,需要重构面向模型的单元测试和模型代码。 要进行重构,只需更新相关的模板并重新生成面向模型的代码即可。

请参阅示例下载中步骤 7 的代码,其中显示了更新后的模板和更新后的面向模型的代码。

总结

希望本文能为您在考虑将面向模型的开发与测试驱动开发相结合时提供一些启发。如果您希望看到对流程描述和示例的进一步澄清和完善,请提供一些反馈。

此外,我希望您能尝试使用 Mo+Mo+ Solution Builder ,以充分利用将面向模型的方法融入您的开发中。这个免费的开源产品可在 moplus.codeplex.com 获取。除了产品本身,该网站还包含用于构建更完整模型和生成完整可用应用程序的示例包。该网站还提供视频教程和其他材料。 Mo+ Solution Builder 也包含广泛的内置帮助。

成为会员!

Mo+ 社区通过网站 https://modelorientedplus.com 获得额外的支持,并为 Mo+ 的发展做出贡献。 成为会员可以为 Mo+ 用户带来额外的好处,例如额外的论坛支持、会员贡献的工具,以及对 Mo+ 发展方向进行投票和/或贡献的能力。此外,我们还将为会员举办月度竞赛,您可以通过使用 Mo+ 开发解决方案来赢取奖金。

如果您对高效的面向模型软件开发有丝毫兴趣,请注册成为会员。 它是免费的,您不会收到垃圾邮件!
© . All rights reserved.