DEV Community

loading...

Help me improve my unit tests

meanin profile image Paweł Ruciński ・2 min read

I am .net developer. I have been using TDD on my daily basis for 3 years now. From time to time, I am finding something new. Something what can possibly improve my skills and even give benefits for my employer. I am also a fluent syntax enthusiast, I use Moq for creating test doubles and fluentassertions for test assertions.

Test layouts

I like including comments following AAA (Arrange, Act, Assert) in my test methods. It makes me think about what is necessary in arrange section, what in act and in assert one as well. I always try to extract as much as possible from assert and put it into arrange section. In example below, there are input value of tested method and expected output extracted.

        [Fact]
        public void GetValue_WhenGivenNumberIs3_ShouldReturnFizz()
        {
            // arrange

            var fizzBuzz = new FizzBuzz.FizzBuzz();
            const int number = 3;
            const string expected = "Fizz";

            // act

            var result = fizzBuzz.GetValue(number);

            // assert

            result.Should().BeEquivalentTo(expected);
        }

The benefit of this extraction is a very well-composed assert section. We do not pay for each letter in our version control system, so for me, it looks like a default approach.

Do you also include this kind of comments in your tests?

Test libraries

Creating test libraries I name unit tests with suffix .Tests, and integration tests with suffix .IntegrationTests. First part of library name is name of tested library. I usually create test class (file) for single source code class. Rarely, when tested class is big enough, or there are so many case scenarios, I split it into several files, each for one method, or for group of methods which follows similar scenarios.

What are your feelings about test libraries? Is there something that I missed?

Naming conventions

Recently, I had a discussion with my coworkers about test method naming convention. We argued about keeping or leaving word "Should" in test method name. This conversation left a deep scratch in my mind, I have been thinking about it so many times in the evenings after work. In our company we are following convention from this article, but personally I like extending it with words When and Should. Take a closer look on it:

  • {MethodName}_{StateUnderTest}_{ExpectedBehavior}
public void GetValue_3_Fizz()
  • {MethodName}_When{StateUnderTest}_Should{ExpectedBehavior}
public void GetValue_WhenGivenNumberIs3_ShouldReturnFizz()

With this approach, name of test method looks like a whole sentence. It makes them self-explanatory even for very young developers. For me, to be honest, test which follows this could be even taken as some kind of a documentation of use cases.

What do you think about it? Should I still use when/should? Would it be good to insist?

And one more question - am I ready to teach younger colleagues in testing approaches?

Discussion

pic
Editor guide
Collapse
alainvanhout profile image
Alain Van Hout
  1. I personally, and the team I'm part of, also inherently follow the AAA pattern, but tend to use simple whitespace delineation rather than repeating the same comments in every test.
  2. Unit tests and integration tests are always given separate namespaces (packages, since we use Java).
  3. We always try to use method names of the form 'methodOrConcern_shouldSomeEffect_whenSomeCause', though we tend to allow for varations in the order of the should and when, and allow 'if' versus 'when' depending on what seems more appropriate in the specific circumstances.
  4. Testing, just like all of programming, is a matter of growing and evolving, which we tend to do as a team. As such, seniors advise juniors, but juniors also offer alternative opinions and suggestions, and in the end everyone learns :).
Collapse
meanin profile image
Paweł Ruciński Author

Hi, thanks for reply.

Referencing to point 1, I am aware of empty spacer approach. Still, I think, that it is easier to involve someone into TDD without any skills, when there is clear distinction between each section.

Collapse
alainvanhout profile image
Alain Van Hout

Fair enough. Within the context of a training exercise that's definitely an important factor. I was mostly talking about 'production code', where superfluous comments tend to decrease the signal-to-noise ratio (if ever so slightly).

That said, I strongly believe in the ability of (appropriately short) comments to add contextual information and remove potential misunderstanding, thereby increasing maintainability. And in the context of training, the AAA comments are exactly that.

Thread Thread
meanin profile image
Paweł Ruciński Author

Thanks again, I will keep that in my mind.

Very good point!

Collapse
jvarness profile image
Jake Varness

That all makes sense to me. I like the clear distinction between tests and integration tests based on the name of the projects, and I like the clear language in the naming of the tests.

When you start teaching your colleagues who are a little greener to unit testing, make sure they understand the concept of writing code that is testable. Writing code that uses good dependency injection patterns is a good practice for implementing any testable system.

If your colleagues are really sharp or if they have exposure to testing and mocking, they will maybe ask how to mock sealed classes, or how to stub out behavior from abstract classes they are extending, and why it may not be the best idea to code themselves into a testing trap by following those patterns themselves.

Collapse
meanin profile image
Paweł Ruciński Author

I will give a try to explaining sealed classes are not so easy to test. Maybe short introduction into wrapping would be enough here?

Collapse
jvarness profile image
Jake Varness

I would say so.

Collapse
markschweiger15 profile image
Mark Schweiger

We add comments most of the time. Maybe it's a bit OCD but we like it that way.

We separate unit and integration tests into 2 different libraries, as running integration tests is less frequent than running unit tests.

The naming convention is fine. But we don't write should as the unit does something.
e.g GetValue_WhenGivenNumberIs3_ReturnsFizz()

The sooner you involve the younger/new devs in unit testing it will serve the team in the long run. Go for it :)

Collapse
tinsoldier6 profile image
Jason Gade

Good article, thanks!

I'm currently using Go, and I find that Table Driven Tests are pretty valuable.

You would set up the table in your Arrange section, and the Act and Assert sections would be mostly combined.