user272671
user272671

Reputation: 657

understanding some unit testing practices

I am a newbie to unit testing - I have only done basic assert tests using mere Testmethods(my last module, I created about 50 of those).

I am currently reading a book on Unit Testing, and one of the many examples in the book has me creating a new class for each single test. Below is one of the example objects created just for one test case. My question is is it ever necessary to do this? Or when should one apply this approach and when is it not necessary?

  public class and_saving_an_invalid_item_type : when_working_with_the_item_type_repository
    {
        private Exception _result;

        protected override void Establish_context()
        {
            base.Establish_context();

            _session.Setup(s => s.Save(null)).Throws(new ArgumentNullException());
        }

        protected override void Because_of()
        {
            try
            {
                _itemTypeRepository.Save(null);
            }
            catch (Exception exception)
            {
                _result = exception;
            }
        }

        [Test]
        public void then_an_argument_null_exception_should_be_raised()
        {
            _result.ShouldBeInstanceOfType(typeof(ArgumentNullException));
        }
    }

Upvotes: 2

Views: 150

Answers (2)

Orion Edwards
Orion Edwards

Reputation: 123662

Do you need to create a new class for each individual test? I would say no, you certainly do not. I don't know why the book is saying that, or if they are just doing it to help illustrate their examples.

To answer your question, I'd recommend using a class for each group of tests... but it's really a bit more complex than that, because how you define "group" is varying and dependant on what you're doing at the time.

In my experience, a set of tests is really logically structured like a document, which can contain one or more set of tests, grouped (and sometimes nested) together by some common aspect. A natural grouping for testing Object-Oriented code is to group by class, and then by method.

Here's an example

  • tests for class 1
    • tests for method 1
      • primary behaviour of method 1
      • alternate behaviour of method 1
    • tests for method 2
      • primary behaviour of method 2
      • alternate behaviour of method 2

Unfortunately, in C# or java (or similar languages), you've only got two levels of structure to work with (as opposed to the 3 or 4 you really actually want), and so you have to hack things to fit.

The common way this is done is to use a class to group together sets of tests, and don't group anything at the method level, as like this:

class TestsForClass1 {
  void Test_method1_primary()
  void Test_method1_alternate()

  void Test_method2_primary()
  void Test_method2_alternate()
}

If both your method 1 and method 2 all have identical setup/teardown, then this is fine, but sometimes they don't, leading to this breakdown:

class TestsForClass1_method1 {
  void Test_primary()
  void Test_alternate()
}

class TestsForClass1_method2 {
  void Test_primary()
  void Test_alternate()
}

If you have more complex requirements (let's say you have 10 tests for method_1, the first 5 have setup requirement X, the next 5 have different setup requirements), then people usually end up just making more and more class names like this:

class TestsForClass1_method1_withRequirementX {  ... }
class TestsForClass1_method1_withRequirementY {  ... }

This sucks, but hey - square peg, round hole, etc.

Personally, I'm a fan of using lambda-functions inside methods to give you a third level of grouping. NSpec shows one way that this can be done... we have an in-house test framework which is slightly different, it reads a bit like this:

class TestsForClass1 {
   void TestsForMethod1() {
      It.Should("perform it's primary function", () => {
         // ....
      });

      It.Should("perform it's alternate function", () => {
         // ....
      });
   }
}

This has some downsides (if the first It statement fails, the others don't run), but I consider this tradeoff worth it.)


-- The question originally read: "is it ever really necessary to create an object for each single test I want to carry out?". The answer to that is (mostly) yes, as per this explanation.

Generally, unit tests involve the interaction of two parts

  • The object under test. Usually this is an instance of a class or a function you've written
  • The environment. Usually this is whatever parameters you've passed to your function, and whatever other dependencies the object may have a reference to.

In order for unit tests to be reliable, both of these parts need to be "fresh" for each test, to ensure that the state of the system is sane and reliable.

  • If the thing under test is not refreshed for each test, then one function may alter the object's internal state, and cause the next test to wrongly fail

  • If the environment is not refreshed for each test, then one function may alter the environment (eg: set some variable in an external database or something), which may cause the next test to wrongly fail.

There are obviously many situations where this is not the case - You might for example have a pure mathematical function that only takes integers as parameters and doesn't touch any external state, and then you may not want to bother re-creating the object under test or the test environment... but generally, most things in any Object-Oriented system will need refreshing, so this is why it is "standard practice" to do so.

Upvotes: 3

I'm not quite able to follow your example, but ideally any test case should be able to run independently of any other - independently from anything else, really.

Upvotes: 2

Related Questions