Multiple Asserts in a Unit Test [duplicate]

六月ゝ 毕业季﹏ 提交于 2019-12-04 00:29:34

It is far more important to test just one concept per unit test.

It may take more than one assertion to test a concept, so don't worry overly about the number of assertions. Of course if you end up with a large number of assertions, you may want to take a step back and think about what you are really testing.

In situations like this, if I'm trying to be strict about one assert per test, I'll assert equality on the Foo, rather than its components. That drives me to write Foo.equals(), and that's usually a good thing in and of itself. But I'm not always strict about one assert per test. See what feels good to you.

AFAI see, the principle behind the practice was

  • to test one cohesive/logical thing per test ; leading to concise and easy to read tests.
  • to have better test isolation. One test should not fail for 1 of n reasons. If it does, then it is not immediately apparent as to how to fix it ; without first investigating which reason caused it to fail. A single change should not fail a truckload of seemingly unrelated tests.
  • a starter-rule from the rulebook for TDD beginners ; so that they can internalize the process.

the point is not that you need one 'NUNit'/'xUnit' assert per test ; rather you have one logical assert per test.

[Test]
public void MakeFoo_SeedsInstanceWithSpecifiedAttributes()
{
  Assert.AreEqual( new Foo(1,2,3), Foo.MakeFoo(1,2,3), "Objects should have been equal );
}

Here I have one NUnit assert (keeping the assert police happy) : However it is testing three things behind the scenes (in Foo.Equals I'd be checking if all the variables are equal.)
The guideline is to prevent tests like (i.e. an integration test masquerading as a unit test)

[Test]
public void ITestEverything()
{
  // humongous setup - 15 lines
  payroll.ReleaseSalaries();
  // assert that the finance department has received a notification
  // assert employee received an email
  // assert ledgers have been updated
  // statements have been queued to the printqueue
  // etc.. etc..
}

Moral of the story: It is a nice target to aim for. Try and compose all the things you want to test into one logical/cohesive Assert with a good name. e.g. Assert.True(AreFooObjectsEqual(f1,f2). If you find that you're having a tough time naming the cohesive verification/assert - maybe you need to review your test and see if you need to split it.

I’ve actually written an NUnit addin to help with this. Try it out at http://www.rauchy.net/oapt

It's common to use multiple asserts in a test, but I don't feel that it's a "best practice."

I would say that yes, you do want to use multiple tests for the above function, exactly so you can see where the problem is. I've recently been working with a fairly large test suite that has some weird failures in it that I have yet to track down, and the problem I'm experiencing is that the test cases each do too much. I've split them up, and now I can disable the specific ones that are failing so that I can get back to them when I have the opportunity.

However, you can still use a fixture to factor out the commonality of your setup and teardown. I can't speak to MSTest, but in UnitTest++ you'd do something like:

class FooFixture
{
public:
   FooFixture() : f(MakeFoo(kX, kY, kZ)) { }

   static const int kX = 1;
   static const int kY = 2;
   static const int kZ = 3;

   Foo f;
};

TEST_FIXTURE(FooFixture, IsXInitializedCorrectly)
{
   CHECK_EQUAL(kX, f.X);
}

// repeat for Y and Z

It's not a ton more typing, especially given cut&paste. Heck, in vim it's just esc y ctrl-{ pp and then minor edits. With this setup, though, you can see if it's just one field failing and then dig in to see why.

I typically find myself having multiple asserts in a method, but they're usually all related to the test at hand. I sometimes chuck in an assert to verify a precondition that I know will break the test because I don't want the test to succeed by accident.

[Test] // NUnit style test.
public void SearchTypeAndInventory() 
{
    var specification = new WidgetSearchSpecification 
                    {
                        WidgetType = Widget.Screw,
                        MinimumInventory = 10
                    }; 
var widgets = WidgetRepository.GetWidgets(specification);
if( !widgets.Any() ) 
    Assert.Inconclusive("No widgets were returned.");
Assert.IsTrue( 
    widgets.All( o => o.WidgetType == Widget.Screw), 
    "Not all returned widgets had correct type");
Assert.IsTrue( 
    widgets.All( o => o.InventoryCount >= 10), 
    "Not all returned widgets had correct inventory count.");

* While I could have combined the Asserts, I find it's more useful to separate what went wrong.


I don't think sticking to a hard rule of 1-assert-per-test is very useful. What's more important is that a test is only testing 1 thing. I've seen many uber-tests that are one enormous method that tests everything about a class. These uber-tests are fragile and hard to maintain.

You should think of your tests as being as important and well-written as the code they are testing. E.g. apply the same refactoring techniques to ensure that a test does one thing and does it well, and isn't a stovepipe that tests everything in sight.

The way I read the books, you're supposed to do "Red, Green, Refactor". In the "Refactor" part, you're supposed to refactor both the code under test and the unit tests.

I see nothing wrong with the following refactoring:

Original

[TestMethod]
public void TestOne()
{
    LetsDoSomeSetup();
    AssertSomething();
}

[TestMethod]
public void TestTwo()
{
    LetsDoSomeSetup(); // Same setup
    AssertSomethingElse();
}

Refactored

[TestMethod]
public void TestOneTwo()
{
    LetsDoSomeSetup();
    AssertSomething();
    AssertSomethingElse();
}

Of course, that assumes that the two asserts are related, and of course relies on the same scenario.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!