如何使用 NUnit 创建一个通用 BaseTest,我可以从中继承并从基本运行中进行测试?

发布于 2024-09-16 22:45:54 字数 1759 浏览 4 评论 0原文

所以基本上我有一个域对象和一个可以对该对象执行 CRUD 操作的通用存储库。

public interface IBaseRepository<T> where T : BaseEntity
{
    void Add(T entity);
    void Remove(T entity);
    T ById(int id);
    IEnumerable<T> All();
}

所以我有这个接口的几个实现,每个域对象都有一个实现。

我想编写一些集成测试(使用 nunit),为此我想我会做一个 BaseRepositoryTest - 像这样:

public abstract class BaseRepositoryTests<T> where T : BaseEntity
{
    public abstract IBaseRepository<T> GetRepository();
    public abstract T GetTestEntity();

    [Test]
    public void AddWhenCallingAddsObjectToDatabase()
    {
        IBaseRepository<T> repository = GetRepository();
        T entity = GetTestEntity();

        repository.Add(entity);
    }
}

现在,对于每个域对象,我必须实现如何初始化存储库以及如何创建测试实体,这似乎很公平,因为它们会有所不同......

我现在要做的就是编写实际的测试装置,对吧?像这样:

[TestFixture]
public class FooRepositoryTests: BaseRepositoryTests<Foo>
{
    public override IBaseRepository<Foo> GetRepository()
    {
        throw new NotImplementedException();
    }

    public override Foo GetTestEntity()
    {
        throw new NotImplementedException();
    }
}

这应该让我开始并给我一个失败的测试,因为抛出会破坏它(我也尝试实际实现这些方法,但没有运气)。但是测试运行器(尝试了 nunits GUI 和 resharpers 测试运行器)只是忽略了我的基本测试!它出现了,但报告返回为“已忽略”。

所以我做了一些挖掘... NUnit 在 TextFixtureAttribute 上有这个属性,可以让你指定你正在测试的类型,所以我尝试将属性

[TestFixture(typeof(Foo))]

首先放在 Base 和 Foo 版本上。当放在 Foo 版本上时,它仍然只是忽略来自底座的测试,而当我将它放在底座上时......它会变成红色,因为这些方法会抛出异常,这会很好,除了即使我进行实际实现时在 FooTests 中,它们仍然无法工作(显然,给定 TestFixture 属性的 Base 测试永远不会知道从它继承的类,所以它如何知道找到实现)。

那么我必须做什么呢?我可以将基测试类中的测试设为虚拟,然后在 FooBaseRepositoryTests 中覆盖它,只是从基调用实现,我认为这是一个蹩脚的解决方案......

还有什么可做的?我错过了什么吗?请有人帮忙...:)

So basically i have a domain object and a generic repository that can do CRUD operations with that object.

public interface IBaseRepository<T> where T : BaseEntity
{
    void Add(T entity);
    void Remove(T entity);
    T ById(int id);
    IEnumerable<T> All();
}

So I have several implementations of this interface, one for each domain object.

I would like to write some integration tests (using nunit) and for that i figured i'd make a BaseRepositoryTest - like this:

public abstract class BaseRepositoryTests<T> where T : BaseEntity
{
    public abstract IBaseRepository<T> GetRepository();
    public abstract T GetTestEntity();

    [Test]
    public void AddWhenCallingAddsObjectToDatabase()
    {
        IBaseRepository<T> repository = GetRepository();
        T entity = GetTestEntity();

        repository.Add(entity);
    }
}

Now, for each domain object i would have to implement how to initialize the Repository and how to create a test entity, which seems fair, given they would be different...

All i have to do now is writing the actual test fixture right? Like this:

[TestFixture]
public class FooRepositoryTests: BaseRepositoryTests<Foo>
{
    public override IBaseRepository<Foo> GetRepository()
    {
        throw new NotImplementedException();
    }

    public override Foo GetTestEntity()
    {
        throw new NotImplementedException();
    }
}

This should get me started and give me a failed test since the throw will break it (i also tried actually implementing the methods with no luck). But the testrunners (tried both nunits GUI and resharpers test runner) just ignore my base test! It shows up and all - but reported back as Ignored.

So i did a little bit of digging... NUnit have this property on the TextFixtureAttribute that lets you specify what kind of you are testing so i tried putting the attribute

[TestFixture(typeof(Foo))]

On first the Base and also the Foo version. When put on the Foo version it still just ignores the test from the base, and when i put it on the base... well it turns red because the methods throws exceptions, which would be good except that even when i do the actual implementation in FooTests, they still won't work (obviously the Base test given the TestFixture attribute would never know what classes inherit from it, so how would it know to find the implementation).

So what am I stuck to do? I could make the test in the base test class virtual and then override it in FooBaseRepositoryTests, only to call the implementation from base, which is a lame solution i think...

What else is there to do? Am I missing something? Please help, someone... :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

掀纱窥君容 2024-09-23 22:45:54

我最近遇到了这个问题,并找到了不同的解决方法。我有一个通用的 IRepository 接口,我希望能够使用多个实现进行测试,因此我创建了一个在设置过程中忽略自身的基类,但此行为被其后代覆盖:

[TestFixture]
public class AbstractRepositoryTests
{
    protected IRepository _repository;

    [SetUp]
    public virtual void SetUp()
    {
        Assert.Ignore();
    }

    [Test]
    public void AddToRepository()
    {
        // Place logic using _repository here
    }
}

然后我覆盖我的后代中的设置行为以实例化存储库对象而不是忽略所有测试:

public class InMemoryRepositoryTests : AbstractRepositoryTests
{
    [SetUp]
    public override void SetUp()
    {
        _repository = new InMemoryRepository<string>();
    }
}

派生类将正确运行其所有父测试。唯一有点混乱的部分是基类创建了一堆“被忽略”的测试,这不是很干净。

I had this issue recently and found a different workaround. I have a generic IRepository interface that I want to be able to test with multiple implementations, so I created a base class that ignores itself during setup, but this behavior is overridden by its descendants:

[TestFixture]
public class AbstractRepositoryTests
{
    protected IRepository _repository;

    [SetUp]
    public virtual void SetUp()
    {
        Assert.Ignore();
    }

    [Test]
    public void AddToRepository()
    {
        // Place logic using _repository here
    }
}

I then override the setup behavior in my descendant to instantiate a repository object rather than ignoring all tests:

public class InMemoryRepositoryTests : AbstractRepositoryTests
{
    [SetUp]
    public override void SetUp()
    {
        _repository = new InMemoryRepository<string>();
    }
}

The derived class will run all of its parent tests properly. The only slightly messy part about this is that the base class creates a bunch of "Ignored" tests, which isn't very clean.

若言繁花未落 2024-09-23 22:45:54

当您在固定装置类上使用属性 [TestFixture(typeof(Foo))] 以便将其用于不同类型时;它不应该是抽象的。

如果在 Foo 固定装置上使用,该类应该是通用的,而不是为 Foo 键入的。

来自文档:

[TestFixture]
public class AbstractFixtureBase
{
    ...
}

[TestFixture(typeof(string))]
public class DerivedFixture<T> : AbstractFixtureBase
{
    ...
}

http://www.nunit.org/ index.php?p=testFixture&r=2.5.5

When you're using the attribute [TestFixture(typeof(Foo))] on the fixture class in order to use it for different types; it's not supposed to be abstract.

If used on the Foo fixture, that class should be generic, and not typed for Foo.

From the docs:

[TestFixture]
public class AbstractFixtureBase
{
    ...
}

[TestFixture(typeof(string))]
public class DerivedFixture<T> : AbstractFixtureBase
{
    ...
}

http://www.nunit.org/index.php?p=testFixture&r=2.5.5

眼眸里的快感 2024-09-23 22:45:54

我还没有尝试过你的例子,但如果你把属性 TestFixture 和 Test 放在同一个类中,你可以尝试一下。也尝试将其放入基类中。

I have not tried out your example but you could try if you put the attributes TestFixture and Test in the same class. Try also to put it into the base class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文