简体   繁体   English

与参数并行运行nunit测试(nunit 3.8.x)

[英]Run nunit tests parallel with parameters (nunit 3.8.x)

I want to run my selenium tests in parallel and set the following in my assembly.cs. 我想并行运行硒测试,并在assembly.cs中设置以下内容。

[assembly: Parallelizable(ParallelScope.Fixtures)]

Ok, fine. 好的。 That works. 这样可行。

Here is a short example of the code structure 这是代码结构的简短示例

using NUnit.Framework;

namespace MyExample
{
    [TestFixture]
    [Category("TestsRunningWithLogin1")]
    public class Test_Example1
    {
        [Test, Order(1)]
        public void Test1()
        {
        }
        [Test, Order(2)]
        public void Test2()
        {
        }
    }

    [TestFixture]
    [Category("TestsRunningWithLogin2")]
    public class Test_Example2
    {
        [Test, Order(1)]
        public void Test1()
        {
        }
        [Test, Order(2)]
        public void Test2()
        {
        }
    }
}

The tests require a username and password and do something in a web page. 测试需要用户名和密码,然后才能在网页中进行操作。 The login etc. is currently handled in a OneTimeSetUp method. 当前在OneTimeSetUp方法中处理登录等。 The webpage saves the last used view in user settings. 该网页将最后使用的视图保存在用户设置中。

If I run the tests sequentially, I have no problems, due to the tests do not influence each other. 如果我按顺序运行测试,那么我不会有问题,因为测试不会相互影响。 All tests can run with the same username. 所有测试都可以使用相同的用户名运行。

If I run it parallel, they could influence each other. 如果并行运行,它们可能会相互影响。 For example test1 configures a view, what should not be seen in test2. 例如,test1配置了一个视图,而在test2中不应看到。

My idea was to run the classes (and there are a lot of them) with different users. 我的想法是与不同的用户一起运行这些类(其中有很多)。 When the test starts, it should take a username, which is currently not used by the parallel runing tests. 测试开始时,它应该使用用户名,并行运行测试当前未使用该用户名。 Currently I don't know which tests are running in parallel by nunit, so I cannot parameterize them directly. 目前,我不知道nunit正在并行运行哪些测试,因此无法直接对其进行参数化。

I did not find anything how to control the parallel tests. 我没有找到如何控制并行测试的任何东西。 I can define, if parallel or not and how many executed in parallel. 我可以定义是否并行以及有多少并行执行。 What I want is, to give the parallel running tests parameters. 我想要的是给并行运行测试参数。 If I have 3 parallel running test classes, I want to give all 3 different parameters. 如果我有3个并行运行的测试类,则要给出所有3个不同的参数。

Any ideas how to achieve that? 有什么想法要实现吗?

What about using a singleton pattern to allocate from a set of passwords based on the threadid. 如何使用单例模式根据线程ID从一组密码中进行分配。

A quick explanation, 快速说明

IThreadCredentials is an interface to describe the credentials, whatever they look like in your case. IThreadCredentials是一个描述凭证的接口,无论您的情况如何。

ThreadCredentials is a simple class I have written that implements IThreadCredentials. ThreadCredentials是我编写的实现IThreadCredentials的简单类。

ICredentialManager is an interface to describe how credentials can be allocated and returned. ICredentialManager是一个界面,用于描述如何分配和返回凭据。

CredentialManager.Instance is the singleton that is shared amongst your fixtures to borrow and return the credentials. CredentialManager.Instance是在灯具之间共享的单例,以借用并返回凭证。

public interface IThreadCredentials
{
    string UserName { get; }

    string Password { get; }
}

public class ThreadCredentials : IThreadCredentials
{
    public ThreadCredentials(string userName, string password)
    {
        this.UserName = userName;
        this.Password = password;
    }

    public string UserName { get; }

    public string Password  { get; }
}

public interface ICredentialManager
{
    IThreadCredentials GetCredentialsFromPool();
    void ReturnCredentialsToPool();
}

public sealed class CredentialManager : ICredentialManager
{
    private static readonly Lazy<CredentialManager> lazy = new Lazy<CredentialManager>(() => new CredentialManager());
    private static readonly object syncRoot = new object ();
    private static readonly Queue<IThreadCredentials> availableCredentialQueue = new Queue<IThreadCredentials>();
    private static readonly IDictionary<int, IThreadCredentials> credentialsByThread = new Dictionary<int, IThreadCredentials>();

    private CredentialManager()
    {
        IEnumerable<IThreadCredentials> availableCredentials = new[]{new ThreadCredentials("Foo", "FooPassword"), new ThreadCredentials("Bar", "BarPassword")};
        foreach (IThreadCredentials availableCredential in availableCredentials)
        {
            availableCredentialQueue.Enqueue(availableCredential);
        }
    }

    public static CredentialManager Instance => lazy.Value;

    public IThreadCredentials GetCredentialsFromPool()
    {
        return GetCredentialsFromPool(Thread.CurrentThread.ManagedThreadId);
    }

    public void ReturnCredentialsToPool()
    {
        ReturnCredentialsToPool(Thread.CurrentThread.ManagedThreadId);
    }

    private static IThreadCredentials GetCredentialsFromPool(int threadId)
    {
        lock (syncRoot)
        {
            IThreadCredentials result;
            if (credentialsByThread.TryGetValue(threadId, out result))
            {
                return result;
            }

            // This presupposes you have enough credentials for the concurrency you are permitting 
            result = availableCredentialQueue.Dequeue();
            credentialsByThread.Add(threadId, result);
            return result;
        }
    }

    private static void ReturnCredentialsToPool(int threadId)
    {
        lock (syncRoot)
        {
            if (credentialsByThread.ContainsKey(threadId))
            {
                IThreadCredentials credentials = credentialsByThread[threadId];
                credentialsByThread.Remove(threadId);
                availableCredentialQueue.Enqueue(credentials);
            }
        }
    }
}

Usage: 用法:

In your test fixture setup, you can do something like: 在测试夹具设置中,您可以执行以下操作:

IThreadCredentials credentials = CredentialManager.Instance.GetCredentialsFromPool();
// Now you can use credentials for whatever

In the teardown, you can then 在拆解中,您可以

CredentialManager.Instance.ReturnCredentialsToPool();
// Then promise you stop using those credentials

Obviously, you will need to have at least the number of credentials available as you intend on running threads in parallel or you will get an exception on dequeue. 显然,在并行运行线程时,您将需要至少具有可用凭据的数量,否则出队会出现异常。

Use nunit TestCase("data") attribute Example: 使用nunit TestCase(“ data”)属性示例:

 [TestCase("differentUserName", "password")]
 public void MyTest(string username, string password)          
 {  
   // Test steps
  }

One possible answer without nunit would be a little service, which delivers the parameters. 如果没有nunit,可能的答案是提供参数的一点服务。 So each parallel test should call a webservice and will get its unique parameters. 因此,每个并行测试都应调用一个Web服务,并将获得其唯一的参数。 The webservice would return with each call the next parameter set. 每次调用时,Web服务将返回下一个参数集。

If I would provide 10 different parameter sets, and run 3 tests in parallel, I can quite be sure, that the 3 parallel tests never get the same parameters. 如果我提供10个不同的参数集并并行运行3个测试,我可以肯定的是,这3个并行测试永远不会获得相同的参数。 Assumed that all test cases needs nearly the same time. 假设所有测试用例几乎都需要相同的时间。

I would call this a hack, therefore I'm asking for a nunit solution. 我会称其为hack,因此我需要一个nunit解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM