Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to organize unit tests and do not make refactoring a nightmare?

My current way of organizing unit tests boils down to the following:

  • Each project has its own dedicated project with unit tests. For a project BusinessLayer, there is a BusinessLayer.UnitTests test project.
  • For each class I want to test, there is a separate test class in the test project placed within exactly the same folder structure and in exactly the same namespace as the class under test. For a class CustomerRepository from a namespace BusinessLayer.Repositories, there is a test class CustomerRepositoryTests in a namespace BusinessLayerUnitTests.Repositories.

Methods within each test class follow simple naming convention MethodName_Condition_ExpectedOutcome. So the class CustomerRepositoryTests that contains tests for a class CustomerRepository with a Get method defined looks like the following:

[TestFixture]
public class CustomerRepositoryTests
{
    [Test]
    public void Get_WhenX_ThenRecordIsReturned()
    {
        // ...
    }

    [Test]
    public void Get_WhenY_ThenExceptionIsThrown()
    {
        // ...
    }
}

This approach has served me quite well, because it makes locating tests for some piece of code really simple. On the opposite site, it makes code refactoring really more difficult then it should be:

  • When I decide to split one project into multiple smaller ones, I also need to split my test project.
  • When I want to change namespace of a class, I have to remember to change a namespace (and folder structure) of a test class as well.
  • When I change name of a method, I have to go through all tests and change the name there, as well. Sure, I can use Search & Replace, but that is not very reliable. In the end, I still need to check the changes manually.

Is there some clever way of organizing unit tests that would still allow me to locate tests for a specific code quickly and at the same time lend itself more towards refactoring?

Alternatively, is there some, uh, perhaps Visual Studio extension, that would allow me to somehow say that "hey, these tests are for that method, so when name of the method changes, please be so kind and change the tests as well"? To be honest, I am seriously considering to write something like that myself :)

like image 770
Nikola Anusev Avatar asked Jul 20 '12 18:07

Nikola Anusev


People also ask

What are best practices for unit testing methods that use cache heavily?

If you want true Unit Tests, then you have to mock the cache: write a mock object that implements the same interface as the cache, but instead of being a cache, it keeps track of the calls it receives, and always returns what the real cache should be returning according to the test case.

What is refactoring in unit testing?

What is refactoring? Code refactoring is the process of restructuring existing computer code – changing the factoring – without changing its external behavior. And how do we make sure we don't break anything during refactoring? Before refactoring a section of code, a solid set of automatic unit tests is needed.

What does it mean to avoid unit testing?

In practice it means avoid as much unit tests as possible and prefer higher level / integration tests more agnostic of implementation details.

How do I refactor my tests?

With refactoring, by definition, you don't change whatyour software does, you change howit does it. Start with all green tests (all pass), then make modifications "under the hood" (e.g. move a method from a derived class to base, extract a method, or encapsulate a Compositewith a Builder, etc.). Your tests should still pass.

Can refactoring break a test?

Fowler literally wrote the book on Refactoring; and the most authoritative book on Unit testing (xUnit Test Patterns by Gerard Meszaros) is in Fowler's "signature" series, so when he says the refactoring can break a test, he's probably right. – perfectionist Jun 20 '16 at 10:57 Add a comment | 5

How to organize and prioritize your test cases?

Keep your work organized (test plans, test cases) so you can come back to it at a later time. For maximum efficiency, organize and prioritize your tests so to have a good overview of all test cases that were made for different features of the software product. As you go, you can build new strategies based on previous tests and create new scenarios.


1 Answers

After working a lot with tests, I've come to realize that (at least for me) having all those restrictions bring a lot of problems in the long run, rather than good things. So instead of using "Names" and conventions to determine that, we've started using code. Each project and each class can have any number of test projects and test classes. All the test code is organized based on what is being tested from a functionality perspective (or which requirement it implements, or which bug it reproduced, etc...). Then for finding the tests for a piece of code we do this:

[TestFixture]
public class MyFunctionalityTests
{
    public IEnumerable<Type> TestedClasses()
    {
        // We can find the tests for a class, because the test cases references in some special method.
        return new []{typeof(SomeTestedType), typeof(OtherTestedType)};
    }

    [Test]
    public void TestRequirement23423432()
    {
        // ... test code.
        this.TestingMethod(someObject.methodBeingTested); //We do something similar for methods if we want to track which methods are being tested (we usually don't)
        // ... 
    }
}

We can use tools like resharper "usages" to find the test cases, etc... And when that's not enough, we do some magic by reflection and LINQ by loading all the test classes, and running something like allTestClasses.where(testClass => testClass.TestedClasses().FindSomeTestClasses()); You can also use the TearDown to gather information about which methods are tested by each method/class and do the same.

like image 152
user1494736 Avatar answered Sep 28 '22 09:09

user1494736