Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I've been thinking about unit testing best practices and have come across the one assertion per unit test rule. I can see where this idea would help to isolate pieces of complex operations or verification when you have code like so:

private Place place;

@Test
public void test_whiteHouse(){
   place = new place(KnownPlaces.WHITEHOUSE);
   assertEquals("1600 Pennsylvania Avenue", place.getAddress());
   assertEquals("Washington", place.getCity());
   assertEquals("DC", place.getState());
}

I see where you would generally want to break it into smaller tests like this:

private Place place;

@Before
public void setUp(){
   place = new place(KnownPlaces.WHITEHOUSE);
}

@Test
public void test_whiteHouse_street(){
   assertEquals("1600 Pennsylvania Avenue", place.getAddress());
}

@Test
public void test_whiteHouse_city(){
   assertEquals("Washington", place.getCity());
}

@Test
public void test_whiteHouse_state(){
   assertEquals("DC", place.getState());
}

However, if I'm testing a setter, the code might look like this:

@Test
public void test_place_setCity(){
   String newCity = "timbuktu";
   Place place = new Place();
   place.setCity(newCity);
   assertEquals(newCity, place.getCity());
}

The test will run properly, but if there's a shared setup, I'm making assumptions about the initial state. What would be the downside on running a check on the initial state something like:

@Test
public void test_place_setCity(){
   String newCity = "timbuktu";
   Place place = new Place();
   assertFalse(place.getCity().equals(newCity));
   place.setCity(newCity);
   assertEquals(newCity, place.getCity());
}

This check would verify that the setter method actually changed something by verifying the initial state was different than the final state. Otherwise, a change or malfunction in a constructor or setup method could cause the test to become ineffective. Are there any downsides to violating the one assert per constraint rule in this way?

share|improve this question

3 Answers 3

up vote 6 down vote accepted

We have a policy where I work that uses an Assume class (provided by Nunit) for all these types of initial checks. Further, we've made a wrapper around the assume class that forces the test writer to reference another test which explicitly tests the assumption. I think I can better describe it with code:

@Test
public void test_place_setCity(){
   // Arrange
   String newCity = "timbuktu";
   Place place = new Place();
   Assume.That(place.getCity(), Is.Not.EqualTo(newCity), validatingTest:"test_new_place_should_not_have_city_set");

   // Act
   place.setCity(newCity);

   // Assert
   assertEquals(newCity, place.getCity());
}

@Test
public void test_new_place_should_not_have_city_set(){
   // Arrange
   // Nothing to Arrange

   // Act
   Place place = new Place();

   // Assert
   Assert.That(place.getCity(), Is.Empty, message:"New cities should not have a city initialized");
}

So here, in our test_place_setCity test, we call Assume, and specify the name of the test that SHOULD fail if the assumption fails. NUnit's assume class returns an "Inconclusive" rather than a "Failing" result when the condition does not succeed. I think these pre-checks are important in many regards. They let future maintainers know WHAT THE TEST ASSUMES to pass up front in an easy to understand way, they FAIL FAST when the assumption doesn't hold because they are at the top of the tests, and they give SEPERATE RESULTS for reporting purposes and to eliminate noise. The name of another test is also a bonus for debugging. Instead of trying to debug why a test won't run because an assumption fails, you can instead, easily navigate to the test that focuses on the assumption and debug using the more focused test.

Hopefully, you can find something in your testing framework to achieve all of these ideals, or else roll something yourself. We've been using this methodology for a while and it's proved very useful.

HTH Marty

share|improve this answer
    
I see! I'm in the java world and JUnit provides a similar Assume function. That's exactly what I was looking for. –  Joshc1107 Nov 8 '12 at 15:41
    
TIL about the Assume function, and that's fantastic. –  Wayne Werner Dec 20 '12 at 20:43

I wouldn't call it a rule but rather a guideline from the past. The common misunderstanding relates to what "one assertion" really means. Unfortunately, some people tend to bend it towards "one assert method call" without really giving it any thought. This is to some extent understandable, after all one of the first guidelines related to unit testing you'll probably hear that "unit test should be atomic, small and test one thing".

However, one assertion is more about logical concept than number of method calls in test. In your example, if all the getters provide simple value that has little to no sense existing outside of its entity, then all 3 asserts relate to one logical concept - your entity. If the getter produces more complex value, it might be worth having its own test. It's always a judgement call.

The code in your example uses well known pattern called guard assertion, and it's nothing wrong with that. Keep in mind however, that guard assertion might be always replaced by extra unit test, as noted here.

share|improve this answer
    
Disagree. Test one thing means test one thing. If I have two setters broken in one class, I might expect two failing tests, which I won't get if I test them all together (unless I use context/specification over AAA). YOU might consider this outdated or overkill, but don't phrase it as if everyone agrees with you. Some people think that testing a setter at all is overkill. –  pdr Nov 8 '12 at 15:52
    
@pdr: I don't consider it (rule) outdated and overkill, I'm saying that it should be taken with caution and more as a guideline than rule. It all narrows down to what you understand under one assertion. Applying it literally to every test might quickly backfire (but that's true to any rule). Care to hint where I sound as if everyone was agreeing with me, as that was not intention of my post? –  jimmy_keen Nov 8 '12 at 16:22
    
Your first two sentences suggest that it's an outdated notion that no one really believes any more. I suggest that people who do believe it have moved to frameworks that support it better, although .NET still lacks a really good C/S test framework. –  pdr Nov 8 '12 at 16:35

In your example, I would not make an assertion about the state of the object before calling the setter. That is outside of the scope of what you are testing. That should have been checked in the initialization tests. The setter/getter test is checking only that functionality so that would be the only assertion that you need.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.