【问题标题】:How to create an automation test for a workflow如何为工作流创建自动化测试
【发布时间】:2020-05-26 10:58:39
【问题描述】:

我正在开发一个工作流项目,该项目有 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();
        }
    }
}

最好的问候。

【问题讨论】:

    标签: c# testing integration-testing mstest


    【解决方案1】:

    嗯,在我看来,您的问题中提供的信息非常抽象,而且问题有点过于宽泛。 答案取决于您的工作流引擎是如何实现的以及您的系统要求是什么。 需求和实施细节决定了您的测试方法。

    我首先要澄清你有什么样的步骤,是否传递了任何数据上下文, 这些步骤会产生什么副作用(将数据写入数据库、发送事件、调用其他系统 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
    }
    

    【讨论】:

      最近更新 更多