[英]How to create an automation test for a workflow
我正在開發一個工作流項目,它有 19 個場景用於測試整個系統和 34 個步驟。
所以,我的問題是,我怎樣才能為它創建一個自動化測試?
我目前的方法是:為每個場景創建一個集成測試,然后創建主系統測試來運行所有集成測試。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace Project1
{
// Unit tests
public class UnitTest_step1
{
public void RunTest() { }
}
public class UnitTest_step2
{
public void RunTest() { }
}
public class UnitTest_step3
{
public void RunTest() { }
}
public class UnitTest_step4
{
public void RunTest() { }
}
// End of unit tests
public class IntegrationTests
{
public void IntegrationTest1()
{
UnitTest_step1.RunTest();
UnitTest_step2.RunTest();
UnitTest_step4.RunTest();
}
public void IntegrationTest2()
{
UnitTest_step1.RunTest();
UnitTest_step2.RunTest();
UnitTest_step3.RunTest();
UnitTest_step4.RunTest();
}
public void IntegrationTest3()
{
UnitTest_step1.RunTest();
UnitTest_step4.RunTest();
}
}
[TestClass]
public class SystemTests
{
[TestMethod]
public void Scenario1()
{
IntegrationTests.IntegrationTest1()
}
[TestMethod]
public void Scenario2()
{
IntegrationTests.IntegrationTest2();
}
[TestMethod]
public void Scenario3()
{
IntegrationTests.IntegrationTest3();
}
[TestMethod]
public void ScenarioN()
{
IntegrationTests.IntegrationTestN();
}
}
}
此致。
好吧,在我看來,您的問題中提供的信息非常抽象,而且問題有點過於寬泛。 答案取決於您的工作流引擎是如何實現的以及您的系統要求是什么。 需求和實施細節定義了您的測試方法。
我首先要澄清你有什么樣的步驟,是否傳遞了任何數據上下文,這些步驟會產生什么副作用(將數據寫入數據庫,發送事件,調用其他系統 API 等),步驟相互依賴等等。
另一個問題是,在每個步驟之后或場景之后,您需要如何斷言結果? 系統應該是可測試的,並且通常每個步驟都應該包含單元測試。 因此,建議的假設方法是用孤立的單元測試和集成測試的場景覆蓋每個步驟。
我想出了一個簡單的例子來說明其中一種通用方法。 為簡單起見,我假設步驟很少或沒有數據上下文,並且可以重新排序。
namespace Workflow.Test
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
[TestClass]
public class SystemTests
{
[TestMethod]
public void Scenario1()
{
new Workflow().Run(new Scenario1());
}
[TestMethod]
public void Scenario2()
{
new Workflow().Run(new Scenario2());
}
// The advantage of explicit steps declaration is test readability.
// Declarative approach also enables the further possibility of test generation!
[TestMethod]
public void MoreExplicitAndDeclarative()
{
new Workflow().Run(new List<Type>
{
typeof(Step1),
typeof(Step2),
typeof(Step3),
});
}
// Step instantiation may be needed if you want to parameterize some steps.
[TestMethod]
[DataRow("Custom step")]
[DataRow("Another step")]
public void MoreExplicitParameterizedScenario(string customName)
{
new Workflow().Run(new List<IRunnable>{
new Step1(),
new Step3(customName)
});
}
}
[TestClass]
public class StepsUnitTests
{
[TestMethod]
public void Step1DoesWhatWeWant()
{
// Mock dependencies
new Step1().Run();
// Assert results
}
}
#region Workflow Engine Example
public interface IRunnable
{
void Run();
}
public class Workflow
{
public void Run(Scenario scenario)
{
Run(CreateSteps(scenario.GetStepTypes()));
}
public void Run(IEnumerable<Type> stepTypes)
{
Run(CreateSteps(stepTypes));
}
public void Run(List<IRunnable> steps)
{
steps.ForEach(step => step.Run());
}
private List<IRunnable> CreateSteps(IEnumerable<Type> stepTypes)
{
var steps = new List<IRunnable>();
foreach (var stepType in stepTypes)
{
steps.Add(CreateStep(stepType));
}
return steps;
}
private IRunnable CreateStep(Type stepType)
=> (IRunnable) Activator.CreateInstance(stepType);
}
#endregion
// Step structure can differ according to system requirements.
// We may add data context and link steps into pipeline if needed.
#region Steps
public abstract class Step : IRunnable
{
private readonly string _stepName;
protected Step(string name)
{
_stepName = name;
}
public void Run()
{
Console.WriteLine($"{_stepName} in action.");
Invoke();
}
public abstract void Invoke();
}
public class Step1 : Step
{
public Step1() : base(nameof(Step1))
{
}
public override void Invoke()
{
// do work
Console.WriteLine($"Step1 invoked.");
}
}
public class Step2 : Step
{
public Step2() : base(nameof(Step2))
{
}
public override void Invoke()
{
// do work
Console.WriteLine($"Step2 invoked.");
}
}
public class Step3 : Step
{
public Step3(string customName) : base(customName)
{
}
public Step3() : this(nameof(Step3))
{
}
public override void Invoke()
{
// do work
Console.WriteLine($"Step3 invoked.");
}
}
public class Step4 : Step
{
public Step4() : base(nameof(Step4))
{
}
public override void Invoke()
{
// do work
Console.WriteLine($"Step4 invoked.");
}
}
#endregion
// Scenarios should be as declarative as possible.
// Let's say the scenario is just specification of what steps (step Type)
// and in what order should be executed (List as a non-unique ordered collection).
#region Scenarios
public abstract class Scenario
{
public abstract List<Type> GetStepTypes();
}
public class Scenario1 : Scenario
{
public override List<Type> GetStepTypes()
=> new List<Type>
{
typeof(Step1),
typeof(Step2),
typeof(Step3)
};
}
public class Scenario2 : Scenario
{
public override List<Type> GetStepTypes()
=> new List<Type>
{
typeof(Step1),
typeof(Step2),
typeof(Step4)
};
}
#endregion
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.