Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/279.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 对这个单元测试感到困惑!_C#_Unit Testing_.net 4.0_Moq_Xunit - Fatal编程技术网

C# 对这个单元测试感到困惑!

C# 对这个单元测试感到困惑!,c#,unit-testing,.net-4.0,moq,xunit,C#,Unit Testing,.net 4.0,Moq,Xunit,所以基本上,我有一个抽象类,它有一个唯一的、递增的ID原语。当一个原语或更准确地说,原语的继承者被实例化时,ID会增加—直到ID溢出—在这一点上,我会向异常添加一条消息并重试 好的,一切都很好。。。但我正在尝试测试这个功能,我以前从未使用过模拟。我只需要创建足够的原语,让ID溢出并断言它在正确的时间抛出 实例化20亿个对象来实现这一点是不合理的!然而,我看不到另一种方式。 我不知道我是否正确使用了嘲弄?我在用最小起订量。 这是我的测试xUnit: 我假设我做错了-那么如何正确地测试它呢?模拟的目

所以基本上,我有一个抽象类,它有一个唯一的、递增的ID原语。当一个原语或更准确地说,原语的继承者被实例化时,ID会增加—直到ID溢出—在这一点上,我会向异常添加一条消息并重试

好的,一切都很好。。。但我正在尝试测试这个功能,我以前从未使用过模拟。我只需要创建足够的原语,让ID溢出并断言它在正确的时间抛出

实例化20亿个对象来实现这一点是不合理的!然而,我看不到另一种方式。 我不知道我是否正确使用了嘲弄?我在用最小起订量。 这是我的测试xUnit:


我假设我做错了-那么如何正确地测试它呢?

模拟的目的是模拟外部资源。这不是你想要的,你想要测试你的对象,在这个szenario中不需要模拟。如果您愿意,只需实例化20亿个对象就行了,因为GC将丢弃旧实例,但可能需要一段时间才能完成

Id'实际上添加了另一个构造函数,该构造函数接受标识计数器的strarting值,这样您就可以在接近int.MaxValue的位置启动,因此不需要重新启动那么多对象


此外,仅从源代码中的readin,我就可以看出您的对象将无法通过测试-

模拟的要点是模拟外部资源。这不是你想要的,你想要测试你的对象,在这个szenario中不需要模拟。如果您愿意,只需实例化20亿个对象就行了,因为GC将丢弃旧实例,但可能需要一段时间才能完成

Id'实际上添加了另一个构造函数,该构造函数接受标识计数器的strarting值,这样您就可以在接近int.MaxValue的位置启动,因此不需要重新启动那么多对象

此外,仅从源代码中的readin,我就可以看出您的对象将无法通过测试-

你不需要为此嘲笑。当两个类一起工作时,您使用mock,并且您希望用mock-fake类替换一个类,因此您只需要测试另一个类。你的例子不是这样的

不过,有一种方法可以使用mock,它可以解决2bln实例的问题。如果将ID生成与基元类分离并使用生成器,则可以模拟生成器。例如:

public abstract class Primitive
{
    internal static IPrimitiveIDGenerator Generator;

    protected Primitive()
    {
        Id = Generator.GetNext();
    }

    internal int Id { get; private set; }
}

public interface IPrimitiveIDGenerator
{
    int GetNext();
}

public class PrimitiveIDGenerator : IPrimitiveIDGenerator
{
    private int? _previousId;

    public int GetNext()
    {
        try
        {
            _previousId = checked(++_previousId) ?? 0;

            return _previousId.Value;
        }
        catch (OverflowException ex)
        {
            throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
        }
    }
}
我已将原语更改为使用提供的生成器。在本例中,它被设置为静态变量,有更好的方法,但作为示例:

public abstract class Primitive
{
    internal static IPrimitiveIDGenerator Generator;

    protected Primitive()
    {
        Id = Generator.GetNext();
    }

    internal int Id { get; private set; }
}

public interface IPrimitiveIDGenerator
{
    int GetNext();
}

public class PrimitiveIDGenerator : IPrimitiveIDGenerator
{
    private int? _previousId;

    public int GetNext()
    {
        try
        {
            _previousId = checked(++_previousId) ?? 0;

            return _previousId.Value;
        }
        catch (OverflowException ex)
        {
            throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
        }
    }
}
然后,您的测试用例变成:

[Fact(DisplayName = "Test Primitive count limit")]
public void TestPrimitiveCountLimit()
{
    Assert.Throws(typeof(OverflowException), delegate()
    {
        var generator = new PrimitiveIDGenerator();

        for (; ; )
        {
            generator.GetNext();
        }
    });
}
这将运行得更快,现在您只需测试ID生成器是否工作

现在,当您想测试创建一个新原语实际上需要ID时,您可以尝试以下操作:

public void Does_primitive_ask_for_an_ID()
{
    var generator = new Mock<IPrimitiveIDGenerator>();

    // Set the expectations on the mock so that it checks that
    // GetNext is called. How depends on what mock framework you're using.

    Primitive.Generator = generator;

    new ChildOfPrimitive();
}
现在,您已经分离了不同的关注点,可以分别测试它们。

您不需要为此进行模拟。当两个类一起工作时,您使用mock,并且您希望用mock-fake类替换一个类,因此您只需要测试另一个类。你的例子不是这样的

不过,有一种方法可以使用mock,它可以解决2bln实例的问题。如果将ID生成与基元类分离并使用生成器,则可以模拟生成器。例如:

public abstract class Primitive
{
    internal static IPrimitiveIDGenerator Generator;

    protected Primitive()
    {
        Id = Generator.GetNext();
    }

    internal int Id { get; private set; }
}

public interface IPrimitiveIDGenerator
{
    int GetNext();
}

public class PrimitiveIDGenerator : IPrimitiveIDGenerator
{
    private int? _previousId;

    public int GetNext()
    {
        try
        {
            _previousId = checked(++_previousId) ?? 0;

            return _previousId.Value;
        }
        catch (OverflowException ex)
        {
            throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
        }
    }
}
我已将原语更改为使用提供的生成器。在本例中,它被设置为静态变量,有更好的方法,但作为示例:

public abstract class Primitive
{
    internal static IPrimitiveIDGenerator Generator;

    protected Primitive()
    {
        Id = Generator.GetNext();
    }

    internal int Id { get; private set; }
}

public interface IPrimitiveIDGenerator
{
    int GetNext();
}

public class PrimitiveIDGenerator : IPrimitiveIDGenerator
{
    private int? _previousId;

    public int GetNext()
    {
        try
        {
            _previousId = checked(++_previousId) ?? 0;

            return _previousId.Value;
        }
        catch (OverflowException ex)
        {
            throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
        }
    }
}
然后,您的测试用例变成:

[Fact(DisplayName = "Test Primitive count limit")]
public void TestPrimitiveCountLimit()
{
    Assert.Throws(typeof(OverflowException), delegate()
    {
        var generator = new PrimitiveIDGenerator();

        for (; ; )
        {
            generator.GetNext();
        }
    });
}
这将运行得更快,现在您只需测试ID生成器是否工作

现在,当您想测试创建一个新原语实际上需要ID时,您可以尝试以下操作:

public void Does_primitive_ask_for_an_ID()
{
    var generator = new Mock<IPrimitiveIDGenerator>();

    // Set the expectations on the mock so that it checks that
    // GetNext is called. How depends on what mock framework you're using.

    Primitive.Generator = generator;

    new ChildOfPrimitive();
}

现在,您已经分离了不同的关注点,可以分别对它们进行测试。

您在这个问题中遇到了两个问题:

如何对无法实例化的抽象类进行单元测试。 如何高效地对需要创建和销毁20亿个实例的功能进行单元测试。 我认为解决方案非常简单,即使你需要稍微重新考虑一下对象的结构

对于第一个问题,解决方案非常简单,只需向测试项目添加一个继承原语但不添加任何功能的伪代码。然后,您可以实例化您的伪类,而您仍将测试原语的功能

对于第二个问题,我将添加一个构造函数,该构造函数对前一个ID使用int,并使用构造函数链接,使您的实际代码中不需要它。但是你怎么知道以前的身份证呢?您不能在测试设置中将其设置为int.MaxValue-1吗?把它想象成依赖注入,但你没有注入任何复杂的东西;您只是注入了一个简单的int。它可以是以下几行:

public abstract class Primitive
{
internal int Id { get; private set; }
private static int? _previousId;

protected Primitive() : Primitive([some way you get your previous id now...])
protected Primitive(int previousId)
{
    _previousId = previousId;
    try
    {
        _previousId = Id = checked (++_previousId) ?? 0;
    }
    catch (OverflowException ex)
    {
        throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
    }
}

这个问题有两个问题:

如何对抽象cl进行单元测试 混蛋,你不能实例化。 如何高效地对需要创建和销毁20亿个实例的功能进行单元测试。 我认为解决方案非常简单,即使你需要稍微重新考虑一下对象的结构

对于第一个问题,解决方案非常简单,只需向测试项目添加一个继承原语但不添加任何功能的伪代码。然后,您可以实例化您的伪类,而您仍将测试原语的功能

对于第二个问题,我将添加一个构造函数,该构造函数对前一个ID使用int,并使用构造函数链接,使您的实际代码中不需要它。但是你怎么知道以前的身份证呢?您不能在测试设置中将其设置为int.MaxValue-1吗?把它想象成依赖注入,但你没有注入任何复杂的东西;您只是注入了一个简单的int。它可以是以下几行:

public abstract class Primitive
{
internal int Id { get; private set; }
private static int? _previousId;

protected Primitive() : Primitive([some way you get your previous id now...])
protected Primitive(int previousId)
{
    _previousId = previousId;
    try
    {
        _previousId = Id = checked (++_previousId) ?? 0;
    }
    catch (OverflowException ex)
    {
        throw new OverflowException("Cannot instantiate more than (int.MaxValue) unique primitives.", ex);
    }
}

其他答案中都提到了这一点。我只是想给你看一个替代方案,也许这对你来说很有趣

当然,如果您将原语类的_previousId字段设置为内部,并包含相应的InternalsVisibleTo属性,那么您的测试就可以通过以下工具实现:

当然,Typemock会带来一些许可成本,但如果您必须编写大量测试代码,尤其是在不易测试或甚至无法使用免费模拟框架进行测试的系统上,它肯定会让您的生活更加轻松,并节省大量时间


托马斯

其他答案都提到了。我只是想给你看一个替代方案,也许这对你来说很有趣

当然,如果您将原语类的_previousId字段设置为内部,并包含相应的InternalsVisibleTo属性,那么您的测试就可以通过以下工具实现:

当然,Typemock会带来一些许可成本,但如果您必须编写大量测试代码,尤其是在不易测试或甚至无法使用免费模拟框架进行测试的系统上,它肯定会让您的生活更加轻松,并节省大量时间


托马斯原语是抽象的,所以我不能直接创建它。我应该只运行测试,而实例化类的一个子类吗?只需创建一个PrimitiveTest:只供测试使用的PrimitiveClass。Primitive是抽象的,所以我不能直接创建它。我应该只运行测试,而实例化类的一个子类吗?只需创建一个PrimitiveTest:仅用于测试的PrimitiveClass。我想到了一个不同的想法并扩展了我的答案。这也解决了2bln实例的问题。关于测试抽象类:为单元测试继承它。只需创建一个虚拟继承类。哇,很好的答案。谢谢你,伙计。我喜欢分离ID生成器的想法,然后我可以在其他地方重用它。不过,我想我需要仔细阅读一下模拟游戏!我想到了一个不同的想法并扩展了我的答案。这也解决了2bln实例的问题。关于测试抽象类:为单元测试继承它。只需创建一个虚拟继承类。哇,很好的答案。谢谢你,伙计。我喜欢分离ID生成器的想法,然后我可以在其他地方重用它。不过,我想我需要仔细阅读一下模拟游戏!为什么捕获OverflowException并用不同的消息抛出相同的异常?您应该忽略这个条件,让框架来处理它。异常不应该被try/catch块用作返回代码。我想这会使在发生故障时更容易修复。也许一个注释更合适。为什么您捕获OverflowException并用不同的消息抛出相同的异常?您应该忽略这个条件,让框架来处理它。异常不应该被try/catch块用作返回代码。我想这会使在发生故障时更容易修复。也许一个评论会更合适。我知道你是从哪里来的,它可以工作。我确实更喜欢彼特的答案,但谢谢你。事实上,彼特的答案更好,因为他不仅对你的目标进行了一些必要的重组,而且还以一种很好的方式分离了关注点。我知道你来自何方,这是可行的。我确实更喜欢彼特的答案,但谢谢你。事实上,彼特的答案更好,因为他不仅对你的目标进行了必要的重组,而且还以一种很好的方式分离了关注点。