自动化测试中的行为设计模式





5.00/5 (5投票s)
了解如何使用行为设计模式来提高自动化测试 API 的灵活性。像搭乐高积木一样构建测试。此文章“自动化测试中的行为设计模式”首次发布于 Automate The Planet。
引言
我认为是时候停止通知您这是最受欢迎系列——自动化测试中的设计模式——的最新一期了。我所说的“行为设计模式”是一种您在所有官方设计模式列表中找不到的设计模式。但如您所知,设计模式是软件设计中常见问题的一种通用可重复解决方案。行为设计模式提高了自动化测试 API 的灵活性。您可以像搭建乐高积木一样构建测试。
定义
行为是小的可重用工作流。它们被组合在不同的测试中。通常,它们由一个特殊的引擎执行。
- 灵活的自动化测试 API
- 改进的测试可维护性
- 提高了测试的可读性
UML 类图
参与者
参与行为设计模式的类和对象是
IBehaviour
- 定义所有行为的接口。包含一个行为可以覆盖的所有方法。Behaviour
- 所有行为的基类,实现了IBehaviour
接口,并提供了所有方法的空虚拟实现。PurchaseTestContext
-静态
类,包含单个购买相关测试的所有相关信息,如 URL、价格和折扣。ItemPageNavigationBehaviour
-ItemPage
类的具体行为。它包含导航逻辑。ItemPage
- 一个具体的页面对象,提供页面上可执行的不同服务操作。它在具体行为中使用。BehaviourEngine
- 执行行为工作流列表的类。
行为设计模式 C# 代码
行为设计模式是我们和我的队友在寻找更好的系统测试设计期间发现的一种模式。您在书中找不到它。有趣的是,当我的经理第一次提出这个想法时,他将该模式命名为乐高设计模式,因为您构建系统测试的方式与构建乐高积木的方式相同。
以前,我们有非常庞大的测试,其中包含大量的复杂工作流。然而,我们经常需要自定义它们。在过去的系统测试设计中,我们使用了外观设计模式。因此,对于每个自定义工作流,我们需要一个不同的外观方法。这导致文件庞大,测试可维护性差,灵活性不高。行为设计模式背后的主要思想是将庞大的工作流分解为多个有意义的小工作流,称为行为。例如,登录可以是一个单独的例程。
IBehaviour 接口
public interface IBehaviour
{
void PerformAct();
void PerformPostAct();
void PerformPostActAsserts();
void PerformPreActAsserts();
}
IBehaviour
接口定义了小的[_]工作流。您可以在 PostAct 阶段执行一个操作,然后等待某事发生。之后,您可以断言页面的状态。此外,您可以在初始操作之前验证状态。
基类行为
public class Behaviour : IBehaviour
{
public virtual void PerformAct()
{
}
public virtual void PerformPostAct()
{
}
public virtual void PerformPostActAsserts()
{
}
public virtual void PerformPreActAsserts()
{
}
}
Behaviour
实现 IBehaviour
接口。它只包含空虚拟方法。如果小[_]具体工作流缺少某些操作,它就不会覆盖它们。
具体行为
public class ItemPageBuyBehaviour : Behaviour
{
private readonly ItemPage itemPage;
public ItemPageBuyBehaviour(ItemPage itemPage)
{
this.itemPage = itemPage;
}
public override void PerformAct()
{
this.itemPage.ClickBuyNowButton();
}
}
我正在自动化的测试用例再次是一个亚马逊在线购买。您可以在我关于空对象设计模式的文章中找到完整的测试用例描述。该行为包含对相关页面对象 ItemPage
的引用。一个初始化的页面实例被传递给构造函数。这是一个简单的[_]工作流,因为它只包含一个操作。然而,在某些情况下,您的行为中可能包含多个操作。例如,应用不同的折扣或断言多个税金。
public class SignInPageLoginBehaviour : Behaviour
{
private readonly SignInPage signInPage;
private readonly ShippingAddressPage shippingAddressPage;
public SignInPageLoginBehaviour(
SignInPage signInPage,
ShippingAddressPage shippingAddressPage)
{
this.signInPage = signInPage;
this.shippingAddressPage = shippingAddressPage;
}
public override void PerformAct()
{
this.signInPage.Login(
PurchaseTestContext.ClientLoginInfo.Email,
PurchaseTestContext.ClientLoginInfo.Password);
}
public override void PerformPostAct()
{
this.shippingAddressPage.WaitForPageToLoad();
}
}
这是一个更复杂的例子。主要操作是登录客户端,然后等待下一页加载。测试相关数据通过静态
类 PurchaseTestContext
传递。
简单的行为引擎
public static class SimpleBehaviourEngine
{
public static void Execute(params Type[] pageBehaviours)
{
foreach (Type pageBehaviour in pageBehaviours)
{
var currentbehaviour = Activator.CreateInstance(pageBehaviour) as Behaviour;
currentbehaviour.PerformPreActAsserts();
currentbehaviour.PerformAct();
currentbehaviour.PerformPostActAsserts();
currentbehaviour.PerformPostAct();
}
}
}
上面是 BehaviourEngine
的最简单实现。我们将当前测试用例的行为按顺序列表传递。然后通过 Activator
类创建行为。之后,它们的[_]操作按预期的工作流顺序执行。
测试中的简单行为引擎
[TestMethod]
public void Purchase_SimpleBehaviourEngine()
{
PurchaseTestContext.ItemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
PurchaseTestContext.ItemPrice = "40.49";
PurchaseTestContext.ClientPurchaseInfo = new ClientPurchaseInfo(
new ClientAddressInfo()
{
FullName = "John Smith",
Country = "United States",
Address1 = "950 Avenue of the Americas",
State = "New York",
City = "New York City",
Zip = "10001-2121",
Phone = "00164644885569"
});
PurchaseTestContext.ClientPurchaseInfo.CouponCode = "99PERDIS";
PurchaseTestContext.ClientLoginInfo = new ClientLoginInfo()
{
Email = "g3984159@trbvm.com",
Password = "ASDFG_12345"
};
SimpleBehaviourEngine.Execute(
typeof(ItemPageNavigationBehaviour),
typeof(ItemPageBuyBehaviour),
typeof(PreviewShoppingCartPageProceedBehaviour),
typeof(SignInPageLoginBehaviour),
typeof(ShippingAddressPageFillShippingBehaviour),
typeof(ShippingAddressPageFillDifferentBillingBehaviour),
typeof(ShippingAddressPageContinueBehaviour),
typeof(ShippingPaymentPageContinueBehaviour),
typeof(PlaceOrderPageAssertFinalAmountsBehaviour));
}
首先,您需要初始化静态 PurchaseTestContext
类。之后,调用 SimpleBehaviourEngine
的 Execute
方法。所需的测试[_]工作流被传递为行为类类型的有序列表。
通用的行为引擎
public static class GenericBehaviourEngine
{
public static void Execute(params Type[] pageBehaviours)
{
foreach (Type pageBehaviour in pageBehaviours)
{
var currentbehaviour = Activator.CreateInstance(pageBehaviour) as Behaviour;
currentbehaviour.PerformPreActAsserts();
currentbehaviour.PerformAct();
currentbehaviour.PerformPostActAsserts();
currentbehaviour.PerformPostAct();
}
}
public static void Execute<t1>()
where T1 : Behaviour
{
Execute(typeof(T1));
}
public static void Execute<t1, t2="">()
where T1 : Behaviour
where T2 : Behaviour
{
Execute(typeof(T1), typeof(T2));
}
public static void Execute<t1, t2="" t3="">()
where T1 : Behaviour
where T2 : Behaviour
where T3 : Behaviour
{
Execute(typeof(T1), typeof(T2), typeof(T3));
}
public static void Execute<t1, t2="">()
where T1 : Behaviour
where T2 : Behaviour
where T3 : Behaviour
where T4 : Behaviour
{
Execute(typeof(T1), typeof(T2), typeof(T3), typeof(T4));
}
// contains 15 more overloads...
public static void Execute<t1, t2="">()
where T1 : Behaviour
where T2 : Behaviour
where T3 : Behaviour
where T4 : Behaviour
where T5 : Behaviour
where T6 : Behaviour
where T7 : Behaviour
where T8 : Behaviour
where T9 : Behaviour
where T10 : Behaviour
where T11 : Behaviour
where T12 : Behaviour
where T13 : Behaviour
where T14 : Behaviour
where T15 : Behaviour
where T16 : Behaviour
where T17 : Behaviour
where T18 : Behaviour
where T19 : Behaviour
where T20 : Behaviour
{
Execute(
typeof(T1),
typeof(T2),
typeof(T3),
typeof(T4),
typeof(T5),
typeof(T6),
typeof(T7),
typeof(T8),
typeof(T9),
typeof(T10),
typeof(T12),
typeof(T13),
typeof(T14),
typeof(T15),
typeof(T16),
typeof(T17),
typeof(T18),
typeof(T19),
typeof(T20),
typeof(T11));
}
}
它基本上是同一个类加上额外的 20 个重载的通用方法。您可以使用这些通用方法,而不是通过 typeof
运算符传递所有类型。
测试中的通用行为引擎
[TestMethod]
public void Purchase_GenericBehaviourEngine()
{
PurchaseTestContext.ItemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
PurchaseTestContext.ItemPrice = "40.49";
PurchaseTestContext.ClientPurchaseInfo = new ClientPurchaseInfo(
new ClientAddressInfo()
{
FullName = "John Smith",
Country = "United States",
Address1 = "950 Avenue of the Americas",
State = "New York",
City = "New York City",
Zip = "10001-2121",
Phone = "00164644885569"
});
PurchaseTestContext.ClientPurchaseInfo.CouponCode = "99PERDIS";
PurchaseTestContext.ClientLoginInfo = new ClientLoginInfo()
{
Email = "g3984159@trbvm.com",
Password = "ASDFG_12345"
};
GenericBehaviourEngine.Execute<
ItemPageNavigationBehaviour,
ItemPageBuyBehaviour,
PreviewShoppingCartPageProceedBehaviour,
SignInPageLoginBehaviour,
ShippingAddressPageFillShippingBehaviour,
ShippingAddressPageFillDifferentBillingBehaviour,
ShippingAddressPageContinueBehaviour,
ShippingPaymentPageContinueBehaviour,
PlaceOrderPageAssertFinalAmountsBehaviour>();
}
它需要更少的代码来配置测试[_]工作流。
覆盖小[_]工作流的一部分
有时,如果您可以只覆盖[_]工作流的一小部分,那会很方便。在某些情况下,您需要为一些不太常见的场景创建测试,所以您不想创建单独的可重用行为。
public class OverridenActionsBehaviourEngine
{
private readonly Dictionary<Type, Dictionary<BehaviourActions, Action>>
overridenBehavioursActions;
public OverridenActionsBehaviourEngine()
{
this.overridenBehavioursActions =
new Dictionary<Type, Dictionary<BehaviourActions, Action>>();
}
public void Execute(params Type[] pageBehaviours)
{
foreach (Type pageBehaviour in pageBehaviours)
{
var currentbehaviour = Activator.CreateInstance(pageBehaviour) as Behaviour;
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.PreActAsserts,
() => currentbehaviour.PerformPreActAsserts());
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.Act,
() => currentbehaviour.PerformAct());
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.PostActAsserts,
() => currentbehaviour.PerformPostActAsserts());
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.PostAct,
() => currentbehaviour.PerformPostAct());
}
}
public void ConfugureCustomBehaviour<TBehavior>(
BehaviourActions behaviourAction,
Action action)
where TBehavior : IBehaviour
{
if (!this.overridenBehavioursActions.ContainsKey(typeof(TBehavior)))
{
this.overridenBehavioursActions.Add(
typeof(TBehavior),
new Dictionary<BehaviourActions, Action>());
}
if (!this.overridenBehavioursActions[typeof(TBehavior)].ContainsKey(
behaviourAction))
{
this.overridenBehavioursActions[typeof(TBehavior)].Add(behaviourAction, action);
}
else
{
this.overridenBehavioursActions[typeof(TBehavior)][behaviourAction] = action;
}
}
private void ExecuteBehaviourOperation(
Type pageBehaviour,
BehaviourActions behaviourAction,
Action defaultBehaviourOperation)
{
if (this.overridenBehavioursActions.ContainsKey(pageBehaviour.GetType()) &&
this.overridenBehavioursActions[pageBehaviour.GetType()].ContainsKey(
behaviourAction))
{
this.overridenBehavioursActions[pageBehaviour.GetType()][behaviourAction].Invoke();
}
else
{
defaultBehaviourOperation.Invoke();
}
}
}
上面是扩展行为引擎的代码,它可以包含被覆盖的部分。
测试中被覆盖操作的行为引擎
[TestMethod]
public void Purchase_OverridenActionsBehaviourEngine()
{
PurchaseTestContext.ItemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
PurchaseTestContext.ItemPrice = "40.49";
PurchaseTestContext.ClientPurchaseInfo = new ClientPurchaseInfo(
new ClientAddressInfo()
{
FullName = "John Smith",
Country = "United States",
Address1 = "950 Avenue of the Americas",
State = "New York",
City = "New York City",
Zip = "10001-2121",
Phone = "00164644885569"
});
PurchaseTestContext.ClientPurchaseInfo.CouponCode = "99PERDIS";
PurchaseTestContext.ClientLoginInfo = new ClientLoginInfo()
{
Email = "g3984159@trbvm.com",
Password = "ASDFG_12345"
};
var behaviourEngine = new OverriddenActionsBehaviourEngine();
behaviourEngine.ConfugureCustomBehaviour<signinpageloginbehaviour>(
BehaviourActions.PostAct,
() => {
// wait for different URL for this case.
});
behaviourEngine.Execute(
typeof(ItemPageNavigationBehaviour),
typeof(ItemPageBuyBehaviour),
typeof(PreviewShoppingCartPageProceedBehaviour),
typeof(SignInPageLoginBehaviour),
typeof(ShippingAddressPageFillShippingBehaviour),
typeof(ShippingAddressPageFillDifferentBillingBehaviour),
typeof(ShippingAddressPageContinueBehaviour),
typeof(ShippingPaymentPageContinueBehaviour),
typeof(PlaceOrderPageAssertFinalAmountsBehaviour));
}
该测试与之前的测试几乎相同,除了 ConfigureCustomBehaviour
方法。在那里,您设置要覆盖的行为的类型。您不是替换整个[_]工作流,只是部分替换,所以您需要配置是哪一部分。最后,您传递一个匿名操作,该操作将代替默认操作执行。
使用 Unity IoC 容器改进行为引擎
public class UnityBehaviourEngine
{
private readonly IUnityContainer unityContainer;
private readonly Dictionary<Type, Dictionary<BehaviourActions, Action>>
overridenBehavioursActions;
public UnityBehaviourEngine(IUnityContainer unityContainer)
{
this.unityContainer = unityContainer;
this.overridenBehavioursActions =
new Dictionary<Type, Dictionary<BehaviourActions, Action>>();
}
public void Execute(params Type[] pageBehaviours)
{
foreach (Type pageBehaviour in pageBehaviours)
{
var currentbehaviour = this.unityContainer.Resolve(pageBehaviour) as Behaviour;
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.PreActAsserts,
() => currentbehaviour.PerformPreActAsserts());
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.Act,
() => currentbehaviour.PerformAct());
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.PostActAsserts,
() => currentbehaviour.PerformPostActAsserts());
this.ExecuteBehaviourOperation(
pageBehaviour,
BehaviourActions.PostAct,
() => currentbehaviour.PerformPostAct());
}
}
public void ConfugureCustomBehaviour<TBehavior>(
BehaviourActions behaviourAction,
Action action)
where TBehavior : IBehaviour
{
if (!this.overridenBehavioursActions.ContainsKey(typeof(TBehavior)))
{
this.overridenBehavioursActions.Add(typeof(TBehavior),
new Dictionary<BehaviourActions, Action>());
}
if (!this.overridenBehavioursActions[typeof(TBehavior)].ContainsKey(behaviourAction))
{
this.overridenBehavioursActions[typeof(TBehavior)].Add(behaviourAction, action);
}
else
{
this.overridenBehavioursActions[typeof(TBehavior)][behaviourAction] = action;
}
}
private void ExecuteBehaviourOperation(
Type pageBehaviour,
BehaviourActions behaviourAction,
Action defaultBehaviourOperation)
{
if (this.overridenBehavioursActions.ContainsKey(pageBehaviour.GetType()) &&
this.overridenBehavioursActions[pageBehaviour.GetType()].ContainsKey(
behaviourAction))
{
this.overridenBehavioursActions[pageBehaviour.GetType()][behaviourAction].Invoke();
}
else
{
defaultBehaviourOperation.Invoke();
}
}
}
Unity IoC 容器代替 Activator
类来解析行为。
测试中的 Unity 行为引擎
[TestMethod]
public void Purchase_UnityBehaviourEngine()
{
PurchaseTestContext.ItemUrl = "/Selenium-Testing-Cookbook-Gundecha-Unmesh/dp/1849515743";
PurchaseTestContext.ItemPrice = "40.49";
PurchaseTestContext.ClientPurchaseInfo = new ClientPurchaseInfo(
new ClientAddressInfo()
{
FullName = "John Smith",
Country = "United States",
Address1 = "950 Avenue of the Americas",
State = "New York",
City = "New York City",
Zip = "10001-2121",
Phone = "00164644885569"
});
PurchaseTestContext.ClientPurchaseInfo.CouponCode = "99PERDIS";
PurchaseTestContext.ClientLoginInfo = new ClientLoginInfo()
{
Email = "g3984159@trbvm.com",
Password = "ASDFG_12345"
};
var behaviourEngine = new UnityBehaviourEngine(container);
behaviourEngine.ConfugureCustomBehaviour<signinpageloginbehaviour>(
BehaviourActions.PostAct,
() => {
// wait for different URL for this case.
});
behaviourEngine.Execute(
typeof(ItemPageNavigationBehaviour),
typeof(ItemPageBuyBehaviour),
typeof(PreviewShoppingCartPageProceedBehaviour),
typeof(SignInPageLoginBehaviour),
typeof(ShippingAddressPageFillShippingBehaviour),
typeof(ShippingAddressPageFillDifferentBillingBehaviour),
typeof(ShippingAddressPageContinueBehaviour),
typeof(ShippingPaymentPageContinueBehaviour),
typeof(PlaceOrderPageAssertFinalAmountsBehaviour));
}
测试中的用法与之前的完全相同,唯一的不同是您需要在容器中注册所有相关的类型。
自动化测试中的设计模式
- 自动化测试中的行为设计模式
- 自动化测试中的高级空对象设计模式
- 自动化测试中的空对象设计模式
- 自动化测试中的高级规格设计模式
- 自动化测试中的规格设计模式
- 自动化测试中的规则设计模式
- 三种设计模式,用于构建更可靠、可维护的 UI 测试(SEETEST 会议 2015 最佳论文奖)
- 自动化测试中改进的外观设计模式 v.2.0
- 使代码更具可维护性的页面对象
- 自动化测试中的装饰器设计模式
- 通过 IObservable 和 IObserver 在自动化测试中实现高级观察者设计模式
- 通过事件和委托在自动化测试中实现高级观察者设计模式
- 观察者设计模式经典实现(自动化测试)
- 自动化测试中的高级策略设计模式
- 自动化测试中的策略设计模式
- 使用 IoC 容器创建更强大的页面对象模式
- 自动化测试中的流畅页面对象模式
- 自动化测试中的单例设计模式
- 自动化测试中的外观设计模式
- 自动化测试中的高级页面对象模式
此文章 自动化测试中的行为设计模式 首次发布于 Automate The Planet。