开发者

unit testing c# properties

开发者 https://www.devze.com 2023-03-02 20:31 出处:网络
I am working with a class that has lots of properties. For example; public class Bib { public int PartQty { get; set; }

I am working with a class that has lots of properties. For example;

public class Bib
{        
    public int PartQty { get; set; }
}

Now to unit test; I did t开发者_StackOverflowhe xUnit test something like

    [Fact]
    public void CanGetAndSetPartQuantity()
    {
        const int expected = 3;

        var target = new Bib() {PartQty = expected};

        Assert.Equal(expected, target.PartQty);
    }

here, I hate how I am hard-coding expected = 3. What's a good way to test this property for accessor and mutator?


Since this property has no behavior other than being a getter/setter for an integer, you're essentially just testing that the compiler worked. This adds basically no value to your test. Consider removing it entirely. That would alleviate you of this odd situation. :)

If you do have some behavior you're trying to capture (e.g., allowed boundary conditions) you only need to test those, and really nothing else. Usually you will have constants for those boundary conditions available as part of the object. Consider using those constants, +/- some appropriate increment.


Constrained Non-determinism is a good fit for this kind of unit test. Write it like this instead:

[Fact]
public void CanGetAndSetPartQuantity()
{
    const int expected = new Random().Next();

    var target = new Bib() {PartQty = expected};

    Assert.Equal(expected, target.PartQty);
}

This ensures that the output correctly represents the input, no matter what the input is.


I'm a firm believer in having unit tests be 'white-box' tests, which means you are allowed to use known corner cases to choose your test inputs. In this particular, case, with an auto-property, the test is unnecessary if you trust your compiler. If you can't trust the compiler to implement an auto-property the way you expect, then you can't trust it to execute the test as you've written, either.

That said, if you have a more complex setter, you would choose your inputs based on the possible failure cases. A few typical cases:

  • Negative numbers for properties that validate >= 0
  • Other validation failures
  • Extreme boundary cases like Int.MaxValue which can sometimes trigger overflows and unexpected behavior in the setter
  • An arbitrary value that should pass validation (no real guidance on how to choose a value here, as long as you know it's in your 'good' case.)


This should help...

[Fact]     
public void CanGetAndSetPartQuantity()     
{
    bool fail = false;
    int expected = 0;

    while (!fail && expected < int.MaxValue)
    {
        var target = new Bib() {PartQty = expected};          
        fail = expected != target.PartQty;
        expected++;
    }

    Assert.IsTrue(!fail);
} 


I wathced a presentation on good unit testing practices a while ago (sorry but the name of the guy escaped my fragile memory). He advocated the use of storing values like that in constants with carefully selected names.

In your case, I would use a name like

const int SomeRandomValidPartQuantity=3;

By this, you signal the intention of using exactly this value, and in this case you are just after any valid quantity.


The test should be derived from some kind of use case. The funny thing is that first you introduced your class, then talked about writing a test, which is backwards to TDD.

The use case informs the test, which informs the code. I highly doubt your use case is "the user of my API can set a property called PartQty to any integer and always get back the integer they set". If that were the real use case, you'd write a unit test that checks int.MaxValue and int.MinValue. However, these are rarely real-world values.

A real-world use case might look like: "the user of my API news up a Bib injecting an IFlugleBinder, sets the PartQty to 4 and then calls the Execute method. This calls the Bind method on the IFlugleBinder instance 4 times." If that was the use case, your test would look very different.

Honestly it looks like Bib is just a DTO of some kind. In my experience, most DTO's are just an artifact of some higher level use case. If the DTO is returned as some result of a function call that your API provides, then you should really be returning an interface, and the DTO class itself should be private, in which case it's not necessary to test it explicitly (just test the properties of the actual result you get from the method call). Likewise, if it's an internal DTO that's never exposed, then don't make it public. If your user has to provide some bundle of values, then your API should be accepting an interface. Let the user define their own class that implements the interface, or provide an immutable one, like this:

public class Bib : IBib
{
    public Bib(int partQty)
    {
        PartQty = partQty;
    }
    public int PartQty { get; private set; }
}

Then you can write a test that checks if your constructor works if you want to be pedantic, but it's not that important.


While I also believe this falls under the "Test till bored" category, if you indeed feel this is worth testing, approval tests offers a very simple way to test. Attached is a simple test to check properties.

[TestMethod]
[UseReporter(typeof(DiffReporter))]
public void TestMethod1()
{
    var fred = new Person{
            Age = 35,
        FirstName = "fred",
        LastName = "Flintstone",
        Hair = Color.Black
           };
    Approvals.Verify(fred.WritePropertiesToString());
}

This will produce a file that reads:

Person
{
    Age: 35
    FirstName: fred
    LastName: Flintstone
    Hair: Color [Black]
}

Simply rename that file to .approved and you're done.

Note the use of the extension method: .WritePropertiesToString()

There a video about the basics of approval tests here for

MsTest: http://www.youtube.com/watch?v=bg8GOmlwqYY

Nunit: http://www.youtube.com/watch?v=aO_fyZBxaFk

Xunit: http://www.youtube.com/watch?v=8wPx0O4gFzc


also you can use autofixture autodata attribute like this:

        [Theory]
        [AutoData]
        public void CanGetAndSetPartQuantity(int expected)
        {
            var target = new Bib() {PartQty = expected};

            Assert.Equal(expected, target.PartQty);
        }
0

精彩评论

暂无评论...
验证码 换一张
取 消