C# “等级制”;“一次性设置”;方法

C# “等级制”;“一次性设置”;方法,c#,nunit,C#,Nunit,好的,我正在编写一些nunit测试来测试API。每当我需要运行这些测试时,我首先需要登录到api以获取令牌。首先,这就是我编写一次性设置的方式 因此,OneTimeSetUp被调用,我登录,一个共享字段存储令牌,每个测试被称为测试api上的不同端点 现在问题来了。我们决定对响应中的各个字段进行单独的测试,以便在出现问题时,我们可以看到确切的结果(并且没有失败)。因此,我们将每个端点划分为它自己的测试 现在,OneTimeSetUp被调用,它登录并调用端点,存储结果,所有测试启动,测试它们的一点点

好的,我正在编写一些nunit测试来测试API。每当我需要运行这些测试时,我首先需要登录到api以获取令牌。首先,这就是我编写一次性设置的方式

因此,OneTimeSetUp被调用,我登录,一个共享字段存储令牌,每个测试被称为测试api上的不同端点

现在问题来了。我们决定对响应中的各个字段进行单独的测试,以便在出现问题时,我们可以看到确切的结果(并且没有失败)。因此,我们将每个端点划分为它自己的测试

现在,OneTimeSetUp被调用,它登录并调用端点,存储结果,所有测试启动,测试它们的一点点

问题是,登录需要时间,并且没有逻辑上的理由说明为什么所有单独的测试不能只使用相同的登录详细信息。有没有办法进一步细分测试/增加额外的测试级别?如果我们能得到这样的测试结果,那就太好了


ApiTests您可以将测试类分组到相同的名称空间中,然后添加一个用SetupFixture属性标记的附加类。这将在每个命名空间中只运行一次初始化代码。(不要与“TestFixtureSetUp”属性混淆,该属性自NUnit v3以来就被标记为过时。感谢Charlie的评论,我最初混淆了它。)

代码示例(一如既往,您可以将每个类放入单独的代码文件中):

“调试所有”测试时,调试窗口中将显示以下输出:

登录。 端点A的测试 端点B的测试
注销。

这里有一种可能的方法来实现这一点

如果您有一个公共基类(正如您描述的那样),您可以创建一个受保护的类来获取令牌,如下例所示

public class ApiTestsBase
{
    protected static Lazy<string> TokenLazy = new Lazy<string>(() =>
                                                             {
                                                                 // Log in and get your API token
                                                                 Console.WriteLine("Logging into API to get token. You should only see this message on the first test that runs"); 
                                                                 return "DEADBEEF";
                                                             });

}

[TestFixture]
public class EndpointATests : ApiTestsBase
{
    private string GetResultFromEndPoint()
    {
        // Call endpoint with token from TokenLazy.Value
        Console.WriteLine($"Calling EndpointA with token {TokenLazy.Value}");
        return "PayloadA";
    }

    [Test]
    public void Test1()
    {
        var payload = this.GetResultFromEndPoint();
        // Assert things about payload
    }

}

[TestFixture]
public class EndpointBTests : ApiTestsBase
{
    private string GetResultFromEndPoint()
    {
        // Call endpoint with token from TokenLazy.Value
        Console.WriteLine($"Calling EndpointB with token {TokenLazy.Value}");
        return "PayloadB";
    }

    [Test]
    public void Test1()
    {
        var payload = this.GetResultFromEndPoint();
        // Assert things about payload
    }

}
公共类ApiTestsBase
{
受保护的静态惰性标记Lazy=新惰性(()=>
{
//登录并获取您的API令牌
WriteLine(“登录API以获取令牌。您应该只在运行的第一个测试中看到此消息”);
返回“死牛肉”;
});
}
[测试夹具]
公共类终结点测试:ApiTestsBase
{
私有字符串GetResultFromEndPoint()
{
//使用TokenLazy.Value中的令牌调用端点
WriteLine($“使用标记{TokenLazy.Value}调用EndpointA”);
返回“PayloadA”;
}
[测试]
公共void Test1()
{
var payload=this.GetResultFromEndPoint();
//断言关于有效载荷的事情
}
}
[测试夹具]
公共类端点测试:ApitessBase
{
私有字符串GetResultFromEndPoint()
{
//使用TokenLazy.Value中的令牌调用端点
WriteLine($“使用标记{TokenLazy.Value}调用EndpointB”);
返回“PayloadB”;
}
[测试]
公共void Test1()
{
var payload=this.GetResultFromEndPoint();
//断言关于有效载荷的事情
}
}
现在我使用字符串类型,但是您可以使用与您的情况相关的任何请求、响应和令牌类型。我怀疑您也可以创造性地将GetResultFromEndPoint调用移动到基类,并使用抽象方法或属性来填充端点特定的细节,但您没有共享足够的代码让我尝试


神奇之处在于static关键字,这意味着每个应用程序域只有一个实例。懒惰者只是将创建延迟到其第一次引用。如果您的测试用例运行很长时间,它会变得更复杂一些,因为您将需要处理令牌更新,但是仍然可以使用一个单例类以类似的方式实现,该类在令牌年龄>x时定期重新验证。如果您的装置没有公共基类,也可以使用单例对象代替上例中的静态。

在NUnit 3之前,
SetUpFixture
属性以相同的方式工作。但是,一次性安装和拆卸方法上的属性名称不同。是否可以将SharedActions传递到端点测试中?如果不做任何静态的诡计,我如何将设置中的相关信息传递到测试类中?我想这在nunit中是不可能的。请看这里:是的,我试图避免使用单例,因为没有任何东西告诉测试运行者“嘿,看,这件事很重要,你可能应该尝试在同一个appdomain中运行所有这些相关测试”。如果它们不在同一个appdomain中,你会如何想象令牌(运行时关注点)会被共享?您必须将其写入磁盘、注册表或其他外部文件才能共享它。可以这样做,但那将是非常脆弱的。我想你已经理解了我的意思——如果我使用了测试运行者知道的某种机制来添加测试之间的关系,那么假设测试运行者会查看这些关系并继续进行测试并不是不合理的“啊!我明白了,你告诉我这些测试都是相关的,并且有一个共享的资源。在这种情况下,出于性能原因,我不会像以前那样将它们拆分为不同的应用程序域“你确定nunit默认就是这么做的吗?我从未见过它这样做。不是NUnit,这是一个测试框架,我指的是测试运行者。NCrunch肯定有这样做的选项,Resharper有指定并行运行多少测试的选项,因此在不同的应用程序域中运行这些测试以专门隔离它们并不是不合理的期望。
public class ApiTestsBase
{
    protected static Lazy<string> TokenLazy = new Lazy<string>(() =>
                                                             {
                                                                 // Log in and get your API token
                                                                 Console.WriteLine("Logging into API to get token. You should only see this message on the first test that runs"); 
                                                                 return "DEADBEEF";
                                                             });

}

[TestFixture]
public class EndpointATests : ApiTestsBase
{
    private string GetResultFromEndPoint()
    {
        // Call endpoint with token from TokenLazy.Value
        Console.WriteLine($"Calling EndpointA with token {TokenLazy.Value}");
        return "PayloadA";
    }

    [Test]
    public void Test1()
    {
        var payload = this.GetResultFromEndPoint();
        // Assert things about payload
    }

}

[TestFixture]
public class EndpointBTests : ApiTestsBase
{
    private string GetResultFromEndPoint()
    {
        // Call endpoint with token from TokenLazy.Value
        Console.WriteLine($"Calling EndpointB with token {TokenLazy.Value}");
        return "PayloadB";
    }

    [Test]
    public void Test1()
    {
        var payload = this.GetResultFromEndPoint();
        // Assert things about payload
    }

}