3

If I were to have a class with a property on it should I be testing the get and set functionalities separately or together?

class MyObject
{
    public string Name { get; set; }
}

[TestClass]
class MyObjectTests
{
    MyObject subject

    [TestInitialize]
    public void TestInitialize()
    {
        subject = new MyObject();
    }

    [TestMethod]
    public void Test_Name()
    {
        string value = "TestValue";
        subject.Name = value;
        Assert.AreEqual(value, subject.Name);
    }
}

In the example above both the getter and setter are being called, so the test passing depends on them both functioning correctly.

Another option could be to test them in different tests, to do this I could set the property via reflection while testing the getter and vice versa. Something like this perhaps.

[TestMethod]
public void Test_Name()
{
    string value = "TestValue";

    var field = typeof(MyObject).GetField("<Name>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
    field.SetValue(subject, value);

    Assert.AreEqual(value, subject.Name);
}

But does this have any real advantages?

I'm torn between the idea that a test should only test a single piece of functionality and the complexities and overhead of using reflection like this over and over throughout my code-base.

Obviously in this example the property is as simple as it possibly could be, but in a class when a setter is actually performing business logic, can/should the tests also rely on the getter? What if they're both performing business logic?

Should I be testing complex accessors with reflection and testing simpler ones together? But what if the simple ones change down the road?

1
  • To me this seems fairly pointless, how the property's value is being stored should not be a concern. You should only be testing the observable behavior. If you were to refactor your code to use a private field for storage instead, the observable behavior doesn't change yet you have to update a bunch of failing unit tests.
    – Matthew
    Apr 11, 2019 at 20:04

4 Answers 4

3

Are there advantages to testing accessors separately?

Sure, like any test there are advantages in making your tests more granular. When they fail, it's clearer about what broke them reducing the time spent debugging test failures. Smaller tests also tend to be easier to write, and easier to write correctly since they tend to be less complex.

That said, the advantages aren't always worth it. More tests in general mean more time to write, run, and maintain. If the properties are simple, then the likelihood of them being broken is small and the cost if they do break might not be worth testing.

Like most testing approaches, it's up to you to weigh the risk involved and make a judgement call about how much risk you're willing to tolerate given the various costs to mitigate it.

5
  • 2
    +1 for "the advantages aren't always worth it". For most getter/setters, the "advantages" of separate tests are almost never worth it.
    – user949300
    Apr 11, 2019 at 20:29
  • well, when they're not trivial getter/setters they're often worth it. But non-trivial getter/setters are (imo) to be avoided, but that's not quite expert consensus enough to be a good answer I think.
    – Telastyn
    Apr 11, 2019 at 20:41
  • It's not uncommon for a setter to validate the input, or to notify about the value change. Non-trivial setters aren't uncommon, in my experience. Trivial getters of the form return m_lastName are relatively common. Apr 11, 2019 at 23:09
  • @user949300: I agree with you, but isn't the main reason for that conclusion that getters and setters should not be using non-trivial logic to begin with? Trivially readable logic does not warrant testing, and if you keep your getters and setters trivially readable (which you should), then they clearly don't need to be tested anyway.
    – Flater
    Apr 14, 2019 at 10:37
  • @NickAlexeev: Input validation or value change notification is easily testable behavior though. You don't actually need access to the internal get/set logic itself, these tests only require you to observe the external behavior of the object whose property you're getting/setting.
    – Flater
    Apr 14, 2019 at 10:39
3

Looking at your simple example

public string Name { get; set; }

I'd argue that the whole test is pointless. It would just test the (.NET) framework and not your own code.

Testing properties at all should only be done if they do have some behaviour that goes beyond merely storing and reading a value, some validation logic for example. Let's look at such a property:

public string MyProperty
{
    get
    {
        return _myProperty;
    }
    set
    {
        // Do some validation here
        _myProperty = value;
    }
}
private string _myProperty;

If you test get and set seperately with your reflection method, what would you really test? You would not only test the behaviour of the property but also an unimportant implementation detail. If for whatever reason _myProperty would be renamed, your test suddenly would break even though the behaviour of the property didn't change.

2
  • (1) OP specifically mentioned that the question is about complex properties with actual business logic in the getter and setter. (2) Even with reflection, you can have refactor-friendly property names by using the nameof() operator (link, take special note of the INotifyPropertyChanged key use case as it's directly applicable to this scenario)
    – Flater
    Apr 14, 2019 at 10:33
  • 1
    @Flater (1) I know, I only used the simple example to illustrate the point of not testing framework/language features, something you mention in your answer yourself. (2) nameof() won't help here. _myProperty is private and therefore not in scope in the test method. Apr 14, 2019 at 11:31
1

Obviously in this example the property is as simple as it possibly could be,

Just to get it out of the way: trivially readable code does not require testing. When you test down to this level, you are effectively testing the framework/language you're using, which is not your responsibility to test. This is effectlively as useful as Assert.AreEqual(1+1, 2), which is to say that it isn't useful testing.

but in a class when a setter is actually performing business logic, can/should the tests also rely on the getter? What if they're both performing business logic?

I'm torn between the idea that a test should only test a single piece of functionality

You're correct, but it's a bit counterintuitive that you hold to this notion while you are also implementing a property's getter and setter with complex and non-abstracted (therefore not separately testable) business logic.

As I said, trivially readable code should not be tested. If you follow that, then your question implies that your getter and setter are both implementing non-trivial logic.

I think you're stacking way too much logical complexity in this property's getters and setters. You're noticing this now because it reduces testability, but it should've thrown up a red flag earlier than that.

Think of it this way: by having complex logic in both the getter and setter, you are effectively doing a secret conversion of your data. For example:

public class Foo
{
    private string mySecretValue;

    public int MyInt
    {
        get { return Convert.ToInt32(mySecretValue); }
        set { mySecretValue = value.ToString(); }
    }
}

This means that anyone who uses this object doesn't need to care about this secret conversion. As long as the conversion is symmetrical, the test should always pass, even if I change the conversion logic. For example, let's say I have the following test:

var foo = new Foo() { MyInt = 123 };

Assert.AreEqual(123, foo.MyInt);

This test would currently pass. And suppose I change my code:

public class Foo
{
    private string mySecretValue;

    public int MyInt
    {
        get { return Convert.ToInt32(mySecretValue).Replace("ZERO", 0); }
        set { mySecretValue = value.ToString().Replace(0, "ZERO"); }
    }
}

The test should still pass, because the consumer of the Foo object does not care about the conversion logic. It only cares about the fact that the conversion is symmetrical, which it still is.

Never forget that tests only care about external behavior. Private logic is by definition private and therefore not needed to be shared outside of the class (which means the test itself cannot rely on knowing a class' private logic).

It shouldn't matter what happens under the hood. As long as it walks like a duck, floats like a duck, and quacks like a duck, it's a duck. Even if under the hood (i.e. in the private logic) it's just a team of miniature people operating a duck-suit.

Should I be testing complex accessors with reflection and testing simpler ones together?

The long and short of it is that you shouldn't have complex accessors. If a bit of logic is less than trivial (and thus valid to test), it should be abstracted into a method of its own. The property can still call the method, and you can write a test which tests that method, without actually needing to worry about the actual property itself.

Besides, who says that this logic wouldn't be useful in more than one property? It makes no sense to make this non-trivial logic not reusable by only keeping it in the property's get/set accessor.

But what if the simple ones change down the road?

As a general rule of thumb, when things change, then you write tests for the no-longer-simple things.

It's a pitfall to think that you should write all of your tests now so they can be used in perpetuity without any further changes or expansions.
Codebases evolve, complexities appear and disappear, and your test suite needs to adapt to these changes as well. Otherwise, your tests are growing apart from the codebase they're supposed to be testing.

0

Another reason not to test getters or setters separately or not testing them at all.

When you test behaviour of the class or function where getters and setters are used you will test getters and setter automatically. So having dedicated tests for getters and setters become redundant.

Unit tests provide confidence to the developer and safety net for refactoring. With separated tests for getters and setters refactoring will become a nightmare, because you will be forced to rewrite tests every time you add remove properties.

20
  • but... that's the point. There's a reason we write many unit tests rather than one big integration test that covers everything.
    – Telastyn
    Apr 11, 2019 at 22:01
  • Surely if an accessor has business logic within it then it testing it would be testing behavior?
    – Toby Smith
    Apr 11, 2019 at 22:04
  • @Telastyn, I am not talking about integration tests. For unit tests you will mock up all external resources which makes tests slow and possibly mock up dependencies which are very very very complicated to configure produce very very big amount of test cases.
    – Fabio
    Apr 11, 2019 at 22:26
  • @Telastyn, in addition: if one big integration test covers everything (usually very rare case) and executes fast enough to provide quick feedback during development - I will be more than happy to have it instead of bunch of unit tests with mocked dependencies. Because I will be able to change my internal design and test will tell me in a seconds did I break something.
    – Fabio
    Apr 11, 2019 at 22:29
  • but not what you broke.
    – Telastyn
    Apr 11, 2019 at 22:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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