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

策略模式-C#

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (7投票s)

2016 年 1 月 4 日

CPOL

5分钟阅读

viewsIcon

25344

这是“策略模式-C#”的替代方案

引言

在我之前的“自动化测试中的设计模式”系列文章中,我详细介绍了如何通过实现页面对象 fachada单例来改进测试自动化框架。当我们必须为更复杂的用例场景编写测试时,遵循开放封闭原则,这是SOLID 的主要原则之一,通常会变得越来越困难。在本系列文章的这一部分,我将使用策略设计模式来为电子商务模块创建可扩展的验证器。

定义

计算机编程中,策略模式(也称为策略模式)是一种软件设计模式,它允许在运行时选择算法的行为。

  • 定义一族算法
  • 封装每个算法
  • 使算法在其族内可互换
  • 代码更易于维护,因为修改或理解策略不需要您理解整个主对象

UML 类图

参与者

参与此模式的类和对象是

  • IStrategyIOrderValidationStrategy)– 定义所有算法的通用接口。上下文类调用该接口来执行由具体策略标识的算法。
  • ConcreteStrategySalesTaxOrderValidationStrategy)– 使用策略接口实现算法。
  • ContextPurchaseContext)– 维护对IStrategy的依赖。包装对具体策略的调用,可以提供访问其数据的接口。

策略设计模式 C# 代码

测试用例

以下测试的主要目标将是购买亚马逊上的不同商品。此外,还应验证购买过程最后一步的价格——税费、运费、礼品包装费等。

  1. 导航到商品页面

  2. 点击去结账
  3. 使用现有客户登录

  4. 填写配送信息

  5. 选择配送速度
  6. 选择付款方式

  7. 验证订单摘要

相关测试类设计的主要目标是实现对购买过程最后一页不同价格的解耦和可扩展的验证。

将使用以下类结构

如您所见,测试的设计大量使用了页面对象模式。不同页面没有不寻常之处,因此我不会在此粘贴每个页面的代码,因为它与文章主题无关。最有趣的逻辑可能位于ShippingAddressPage中。

public class ShippingAddressPage : BasePageSingleton<ShippingAddressPage, ShippingAddressPageMap>
{
    public void ClickContinueButton()
    {
        this.Map.ContinueButton.Click();
    }

    public void FillShippingInfo(ClientPurchaseInfo clientInfo)
    {
        this.Map.CountryDropDown.SelectByText(clientInfo.Country);
        this.Map.FullNameInput.SendKeys(clientInfo.FullName);
        this.Map.Address1Input.SendKeys(clientInfo.Address1);
        this.Map.CityInput.SendKeys(clientInfo.City);
        this.Map.ZipInput.SendKeys(clientInfo.Zip);
        this.Map.PhoneInput.SendKeys(clientInfo.Phone);
        this.Map.ShipToThisAddress.Click();
    }
}

ClientPurchaseInfo类保存有关客户购买的数据,大部分数据通过string属性填充。

public class ClientPurchaseInfo
{
    public string FullName { get; set; }

    public string Country { get; set; }

    public string Address1 { get; set; }

    public string City { get; set; }

    public string Phone { get; set; }

    public string Zip { get; set; }

    public string Email { get; set; }

    public string State { get; set; }

    public string DeliveryType { get; set; }

    public GiftWrappingStyles GiftWrapping { get; set; }
}

未使用策略设计模式的实现

可以通过使用 fachada 设计模式轻松自动化用例。

public class PurchaseFacade
{
    public void PurchaseItemSalesTax(string itemUrl, string itemPrice, 
    string taxAmount, ClientLoginInfo clientLoginInfo, ClientPurchaseInfo clientPurchaseInfo)
    {
        PurchaseItemInternal(itemUrl, clientLoginInfo, clientPurchaseInfo);
        PlaceOrderPage.Instance.Validate().EstimatedTaxPrice(taxAmount);
    }

    public void PurchaseItemGiftWrapping(string itemUrl, string itemPrice, 
    string giftWrapTax, ClientLoginInfo clientLoginInfo, ClientPurchaseInfo clientPurchaseInfo)
    {
        PurchaseItemInternal(itemUrl, clientLoginInfo, clientPurchaseInfo);
        PlaceOrderPage.Instance.Validate().GiftWrapPrice(giftWrapTax);
    }

    public void PurchaseItemShippingTax(string itemUrl, string itemPrice, 
    string shippingTax, ClientLoginInfo clientLoginInfo, ClientPurchaseInfo clientPurchaseInfo)
    {
        PurchaseItemInternal(itemUrl, clientLoginInfo, clientPurchaseInfo);
        PlaceOrderPage.Instance.Validate().ShippingTaxPrice(shippingTax);
    }

    private void PurchaseItemInternal(string itemUrl, 
    ClientLoginInfo clientLoginInfo, ClientPurchaseInfo clientPurchaseInfo)
    {
        ItemPage.Instance.Navigate(itemUrl);
        ItemPage.Instance.ClickBuyNowButton();
        PreviewShoppingCartPage.Instance.ClickProceedToCheckoutButton();
        SignInPage.Instance.Login(clientLoginInfo.Email, clientLoginInfo.Password);
        ShippingAddressPage.Instance.FillShippingInfo(clientPurchaseInfo);
        ShippingAddressPage.Instance.ClickContinueButton();
        ShippingPaymentPage.Instance.ClickBottomContinueButton();
        ShippingPaymentPage.Instance.ClickTopContinueButton();
    }
}

这种解决方案的主要缺点是不同税务验证情况下的方法数量。如果需要引入新的税务验证,则需要添加一个新方法,这将违反开放封闭原则

策略设计模式实现

在这个示例电子商务模块中,有数百个可以自动化的测试用例。其中一些最重要的与购买过程最后一页价格正确性有关。因此,测试中验证的主要目标将是测试是否显示了正确的价格。有几种类型的税费——销售税(美国/加拿大)、增值税(欧盟国家)、礼品包装、运费等。页面对象可以组合在PurchaseContext类中执行新的购买,可以应用策略设计模式来传递特定的验证策略。

验证策略的主要方法可以通过以下接口定义。

public interface IOrderValidationStrategy
{
    void ValidateOrderSummary(string itemPrice, ClientPurchaseInfo clientPurchaseInfo);
}

PurchaseContext类与已讨论的 fachada 类几乎相同;唯一的区别是它维护对IOrderValidationStrategy的依赖。

具体的验证策略通过其构造函数传递。

public class PurchaseContext
{
    private readonly IOrderValidationStrategy orderValidationStrategy;

    public PurchaseContext(IOrderValidationStrategy orderValidationStrategy)
    {
        this.orderValidationStrategy = orderValidationStrategy;
    }

    public void PurchaseItem(string itemUrl, string itemPrice, 
    ClientLoginInfo clientLoginInfo, ClientPurchaseInfo clientPurchaseInfo)
    {
        ItemPage.Instance.Navigate(itemUrl);
        ItemPage.Instance.ClickBuyNowButton();
        PreviewShoppingCartPage.Instance.ClickProceedToCheckoutButton();
        SignInPage.Instance.Login(clientLoginInfo.Email, clientLoginInfo.Password);
        ShippingAddressPage.Instance.FillShippingInfo(clientPurchaseInfo);
        ShippingAddressPage.Instance.ClickContinueButton();
        ShippingPaymentPage.Instance.ClickBottomContinueButton();
        ShippingPaymentPage.Instance.ClickTopContinueButton();
        this.orderValidationStrategy.ValidateOrderSummary(itemPrice, clientPurchaseInfo);
    }
}

在大多数情况下,我认为验证税费和类似货币金额的最佳方法是调用实际的生产 Web 服务,这些服务用于驱动实际的电子商务模块。它们应该已经通过单元和集成测试完全测试,并且其输出应该是保证的。

E2E 测试的主要目标是确保在页面上显示正确的价格,而不是检查实际的计算逻辑。因此,在我对SalesTaxOrderValidationStrategy的具体实现中,我创建了一个虚拟的示例实现。在实际测试中,我们应该调用生产 Web 服务,而不是手动计算税费。如果模块不使用 Web 服务,您始终可以创建自己的测试 Web 服务,并在包装方法中调用生产代码。我曾为我必须自动化的一个旧遗留模块做过这件事。

public class SalesTaxOrderValidationStrategy : IOrderValidationStrategy
{
    public SalesTaxOrderValidationStrategy()
    {
        this.SalesTaxCalculationService = new SalesTaxCalculationService();
    }

    public SalesTaxCalculationService SalesTaxCalculationService { get; set; }

    public void ValidateOrderSummary(string itemsPrice, ClientPurchaseInfo clientPurchaseInfo)
    {
        States currentState = (States)Enum.Parse(typeof(States), clientPurchaseInfo.State);
        decimal currentItemPrice = decimal.Parse(itemsPrice);
        decimal salesTax = this.SalesTaxCalculationService.Calculate
				(currentItemPrice, currentState, clientPurchaseInfo.Zip);

        PlaceOrderPage.Instance.Validate().EstimatedTaxPrice(salesTax.ToString());
    }
}

我为增值税验证策略实现了一个类似的逻辑。

public class VatTaxOrderValidationStrategy : IOrderValidationStrategy
{
    public VatTaxOrderValidationStrategy()
    {
        this.VatTaxCalculationService = new VatTaxCalculationService();
    }

    public VatTaxCalculationService VatTaxCalculationService { get; set; }

    public void ValidateOrderSummary(string itemsPrice, ClientPurchaseInfo clientPurchaseInfo)
    {
        Countries currentCountry = (Countries)Enum.Parse(typeof(Countries), clientPurchaseInfo.Country);
        decimal currentItemPrice = decimal.Parse(itemsPrice);
        decimal vatTax = this.VatTaxCalculationService.Calculate(currentItemPrice, currentCountry);

        PlaceOrderPage.Instance.Validate().EstimatedTaxPrice(vatTax.ToString());
    }
}

使用策略设计模式的一个重要好处是,如果应用程序引入了新的税费,您无需修改现有页面或PurchaseContext。您只需要创建一个新的具体策略。

例如,如果亚马逊宣布从明天开始,一位超级名模可以将您想要的商品直接送到您家门口,只需 100 美元。您可以通过创建一个新的SuperModelOrderValidationStrategy来处理新的税费验证。想象一下布拉德·皮特背着冰箱送货,我愿意花钱看这个。

 

使用策略设计模式的测试

[TestClass]
public class AmazonPurchase_PurchaseStrategy_Tests
{       
    [TestInitialize]
    public void SetupTest()
    {
        Driver.StartBrowser();
    }

    [TestCleanup]
    public void TeardownTest()
    {
        Driver.StopBrowser();
    }

    [TestMethod]
    public void Purchase_SeleniumTestingToolsCookbook()
    {
        string itemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
        string itemPrice = "40.49";
        ClientPurchaseInfo clientPurchaseInfo = new ClientPurchaseInfo()
        {
            FullName = "John Smith",
            Country = "United States",
            Address1 = "950 Avenue of the Americas",
            State = "New York",
            City = "New York City",
            Zip = "10001-2121",
            Phone = "00164644885569",
            GiftWrapping = Enums.GiftWrappingStyles.None
        };
        ClientLoginInfo clientLoginInfo = new ClientLoginInfo()
        {
            Email = "g3984159@trbvm.com",
            Password = "ASDFG_12345"
        };

        new PurchaseContext(new SalesTaxOrderValidationStrategy()).PurchaseItem
        	(itemUrl, itemPrice, clientLoginInfo, clientPurchaseInfo);
    }
}

使用策略设计模式来创建自动化测试很简单。您需要做的就是将所需的算法传递给新创建的PurchaseContext

到目前为止,“自动化测试中的设计模式”系列

  1. 页面对象模式
  2. 高级页面对象模式
  3. 门面设计模式
  4. Singleton 设计模式
  5. 流畅页面对象模式
  6. IoC 容器与页面对象
  7. 策略设计模式
  8. 高级策略设计模式
  9. 观察者设计模式
  10. 通过事件和委托实现观察者设计模式
  11. 通过 IObservable 和 IObserver 实现观察者设计模式
  12. 装饰器设计模式 - 混合策略
  13. 使代码更具可维护性的页面对象
  14. 改进的自动化测试外观设计模式 v.2.0
  15. 规则设计模式
  16. 规格设计模式
  17. 高级规格设计模式

源代码

如果您喜欢这篇文章,请点击这些分享按钮。谢谢!

参考文献

所有图片均购自DepositPhotos.com,不得免费下载和使用。许可协议

帖子——自动化测试中的策略设计模式——首次出现在Automate The Planet

© . All rights reserved.