最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to avoid logic in data-driven unit tests asserts when testing exceptions (and should I)? - Stack Overflow

programmeradmin2浏览0评论

I'm using C# + xUnit, but my question can be applied to any language/test framework.

Per MSDN:

Avoid logic in tests

When writing your unit tests, avoid manual string concatenation, logical conditions, such as if, while, for, and switch, and other conditions.

Why? Less chance to introduce a bug inside of your tests. Focus on the end result, rather than implementation details. When you introduce logic into your test suite, the chance of introducing a bug into it increases dramatically. The last place that you want to find a bug is within your test suite. You should have a high level of confidence that your tests work, otherwise, you won't trust them. Tests that you don't trust, don't provide any value. When a test fails, you want to have a sense that something is wrong with your code and that it can't be ignored.

Tip If logic in your test seems unavoidable, consider splitting the test up into two or more different tests.

So in xUnit, we have theories that allow to stub different data as arguments. And it's fine for many cases like:

[Theory]
[InlineData(1, 2, 3)]
public void Sum_ShouldBeOk(int lhs, int rhs, int expectedResult)
{
    // arrange
    var calc = new Calculator();

    // act
    var result = calc.Sum(lhs, rhs);

    // assert
    Assert.Equal(result, expectedResult);
}

But what if I should test whether an exception is thrown or not? With FluentAssertions, it looks like this:

[Theory]
[InlineData(Role.User, true)]
[InlineData(Role.Admin, false)]
public void SpendAllMoney_ShouldNotBeAllowedForEveryone(Role role, bool shouldThrow)
{
    // arrange
    var wallet = new Wallet(100000M);
    var user = new User(role);

    // act
    var act = () => wallet.Withdraw(user, "some wallet", 100000M);

    // assert
    if (shouldThrow)
    {
        act.Should().Throw<AccessDeniedException>();
    }
    else
    {
        act.Should().NotThrow();
    }
}

In this case, branching seems unavoidable because there's no single method to check whether the exception is thrown or not: unlike returns, this is a different flow of program execution. To fix it I should copy-paste the whole test? Which may have quite a big setup specific for this test? So in order to fix the potential bug that can be cause by logic in tests, I should introduce code duplication that can cause its own bugs? Or make a method “setup whatever case” and call it in different methods?

It seems like extra work for the sake of following the best practices that doesn't solve anything.

发布评论

评论列表(0)

  1. 暂无评论