TDD - Am I doing it right?

I have a class that deals with the Account stuff. It provides methods for logging in, password reset, and creating new accounts.

I am injecting dependencies through the constructor. I have tests that check each dependency link, if the link is null it throws an ArgumentNullException.

The Account class exposes each of these dependencies through read-only properties. Then I have tests that check if the reference passed by the constructor is the same as the property being returned. I do this to make sure the references are kept in the class. (I don't know if this is good practice either.)

First question: Is this good practice in TDD? I ask this because this class has 6 dependencies so far and it is getting very repetitive and also the tests are getting quite long since I have to mock all the dependencies for each test. What I am doing is just copy and paste every time and just change the checked dependency reference.

Second question, my method of creating an account does things like validating the passed model, inserting data into 3 different tables, or a fourth table if a specific set of values ​​is present, and sending an email. What should I check here? I have tested so far, checks if the model validation is done if the Add method is called on each repository, in which case I use the Moq callback method from the mocked repository to compare each property added to the repository against the ones I went through models.

Something like:

    userRepository
        .Setup(r => r.Add(It.IsAny<User>()))
        .Callback<User>(u =>
            {
                Assert.AreEqual(model.Email, u.Email);
                Assert.IsNotNull(u.PasswordHash);
                //...
            })
        .Verifiable();

      

As I said, these tests are getting longer, I think it doesn't hurt me to test anything that I can, but I don't know if it's worth it as it takes time to write tests.

+3


source to share


3 answers


The goal of testing is to find bugs.

Do you really get an error where the property exists but is not initialized with a value from the constructor?

public class NoNotReally {
    private IMyDependency1 _myDependency;
    public IMyDependency1 MyDependency {get {return _myDependency;}}

    public NoNotReally(IMyDependency dependency) {
        _myDependency = null; // instead of dependency. Really?
    }
}

      



Also, since you are using TDD, you must write tests before writing the code, and the code must exist only for the tests to pass. Instead of your unnecessary property tests, write a test to demonstrate that your injectable dependency is being used. To run or pass such a test, a dependency must exist, it must be correct, and it must be used in a specific scenario.

In my example, the dependency will exist because it is needed, not because some artificial unit test required it to be there.

+4


source


You say that writing these tests seems repetitive. I am saying that you feel the great benefits of TDD. This is not really less buggy software and not writing better software because TDD does not guarantee (at least not inherently). TDD makes you think about design decisions and make design decisions .. Time. (And reduce debugging time.) If you feel pain while doing TDD, it's usually because a design decision comes back to bite you. Then it's time to switch to your refactoring hat and improve your design.

Now, in this particular case, it's just the design of your tests, but you also have to make design decisions for them.

How to check if properties are set. If I understand you correctly, did you set these properties just for testing purposes? In this case, I would suggest doing it. Suppose you have a class with a constructor parameter and you have a test that asserts that construtor must use null arguments:

public class MyClass
{
    public MyClass(MyDependency dependency)
    {
        if (dependency == null)
        {
            throw new ArgumentNullException("dependency");
        }
    }
}

[Test]
public void ConstructorShouldThrowOnNullArgument()
{
    Assert.Catch<ArgumentNullException>(() => new MyClass(null));
}

      

(TestFixture class omitted)



Now, when you start writing a test for the actual business method of the class under test, the pieces will start to fit together.

[Test]
public void TestSomeBusinessFunctionality()
{
    MyDependency mockedDependency;

    // setup mock
    // mock calls on mockedDependency

    MyClass myClass = new MyClass(mockedDependency);

    var result = myClass.DoSomethingOrOther();

    // assertions on result
    // if necessary assertion on calls on mockedDependency
}

      

At this point, you will have to assign a nested constructor dependency in the field in order to use it later in the method. And if you can manage to pass the test without using the dependency ... well heck, obviously you didn't need that to get started. Or maybe you're just starting to use it for your next test.

About other. When it becomes a problem to validate all the responses of a method or class, TDD tells you that the method / class does a lot and might like to be split into parts that are easy to test. For example. one class for validation, one for mapping, and one for making storage calls.

However, this can lead to over-engineering! So keep an eye on it and you will have a feeling when you need to resist the urge for more indirection.;)

To test if the property mapping is correct, I would suggest using stubs or homemade fake objects that have simple properties. This way you can just compare the source and target properties and not create lengthy settings like the ones you posted.

+3


source


Typically in unit tests (especially in TDD), you won't test every statement in the class under test. The main purpose of TDD unit tests is to test the business logic of the class, not the initialization stuff.

In other words, you give scripts (don't forget to include edge cases as well) as input and validate the results, which can be either final property values ​​and / or method return values.

The reason you don't want to test every possible code path in your classes is that if you ever want to refactor your classes later, you only need to make minimal changes to your TDD test cases as they are supposed to be agnostic for the actual implementation (as far as possible).

Note. There are other types of unit tests, such as code coverage tests, which are designed to test every single piece of code in your classes. However, I personally find these tests impractical and certainly discouraged in TDD.

+1


source







All Articles