开发者

First Unit Test (VS2010 C#)

开发者 https://www.devze.com 2023-02-10 07:19 出处:网络
This is my first encounter with unit testing and I am trying to understand how can this concept be used on开发者_StackOverflow中文版 a simple date validation.

This is my first encounter with unit testing and I am trying to understand how can this concept be used on开发者_StackOverflow中文版 a simple date validation.

The user can select a ToDate that represents the date until a payment can be made. If our date is not valid the payment cant be made.

    private void CheckToDate(DateTime ToDate)
    {
        if (Manager.MaxToDate < ToDate.Year)
            //show a custom message
    }

How can unit tests be used in this case?

Regards,

Alex

Thanks for your answers:

As suggested by many of you I will split the function and separate the validation from the message display and use unit tests just for this.

public bool IsDateValid(DateTime toDate)
{
    return (Manager.MaxToDate < toDate.Year);
}


Yes it is possible. But unit testing changes design of your class. To make possible unit testing of this code, you should made following changes:

  1. Make your method public. (It is possible to make it protected, but for simplicity make it public).

  2. Extract all external dependencies of this method to interface, so you can mock them. Then you can use some mocking library (moq, Rhino.Mocks) to simulate real dependencies and write asserts.

  3. Write test.

Here is sample code.

The class under test:

public class ClassUnderTest
{
    public IManager Manager {get;set;}
    public IMessanger Messanger {get;set}

    public  ClassUnderTest (IManager manager, IMessanger messanger)
    {
        Manager = manager;
        Messanger = messanger;
    }

    private void CheckToDate(DateTime ToDate)
    {
        if (Manager.MaxToDate < ToDate.Year)
            //show a custom message
            Messanger.ShowMessage('message');
    }
}

Test:

[TestFixture]
public class Tester
{
    public void MessageIsShownWhenDateIsLowerThanMaxDate()
    {
        //SetUp
        var manager = new Mock<IManager>();
        var messanger = new Mock<IMessanger>();

        var maxDate = DateTime.Now;

        manager.Setup(m => m.MaxToDate).Returns(maxDate);

        var cut = new ClassUnderTest (manager.Object, messanger.Object);

        //Act
        cut.CheckToDate();

        //Assert
        messanger.Verify(foo => foo.ShowMessage("message"), Times.AtLeastOnce())
    }
}

Design change, introduced by test gives you nice decoupling in system. And tests could be written for specific classes, when external dependencies are event not written.


Sure thing :-) Detecting that the custom message is shown can require a little trick (I assume you mean a messagebox displayed on a GUI, but the idea is the same even if the message is displayed differently).

You can't detect mssage boxes from unit tests, neither you want to launch the whole GUI environment from your unit tests. The easiest way to work around this is to hide the actual code displaying a message box in a separate method, ideally in a distinct interface. Then you can inject a mock implementation of this interface for your unit tests. This mock does not display anything, just records the message passed to it, so you can check it in your unit test.

The other issue is that your method is private. Check first where it is called from, and whether it can be called via a public method without too much complication. If not, you may need to make it (temporarily) public to enable unit testing. Note that the need to unit test private methods is usually a design smell: your class may be trying to do too much, taking on too many distinct responsibilities. You may be able to extract some of its functionality into a distinct class, where it becomes public, thus directly unit testable. But first you need to have those unit tests, to ensure you are not breaking anything when refactoring.

You then need to set up the Manager.MaxToDate prior to the test with a suitable date, and call CheckToDate with various parameters, check that the result is as expected.

The recommended reading for similar tricks and more is Working Effectively with Legacy Code.


Unit testing is best done on the public interface of your classes. So, I'd suggest you either make this public, or look to test it indirectly (through the public methods you do expose).

As for "Is it possible to create unit tests for something like this?", it depends on how pure you want to be on the concept of Unit Tests, how user-dependent you want them to be, and what exactly //show a custom message does.

How pure do you want your unit tests to be? If you don't care if they are dirty hacks, then you can use reflections to expose the private method to your unit tests, and just call it directly. This is in general a bad practice, though, because your private functions by definition are subject to change. Otherwise you'd just make them public.

If //show a custom message prints to the console, then you can make silent-running tests fairly easily. If you actually want to verify the output, you'd have to hook into your Console.Out, so you can see what got printed, and add corresponding assertions.

If //show a custom message uses MessageBox.Show, then you may have to make a UI Automated Test to be able to test this. Your tests will not be able to run silently in the background, and will break if you're moving your mouse while the test is running.

If you don't want to make a UI Automated Test just to test the logic of this class, the best way I know of is to modify your class to use dependency injection. Encapsulate all of the actual output code (MessageBox.Show) into another class, abstract it via an interface or abstract base class, and make it so your original class takes a reference to the abstract type. This way you can inject a mock in your tests, and it won't actually output to the screen.

public interface INotification
{
    void ShowMessage(string message);
}

public class MessageBoxNotification : INotification
{
    public void ShowMessage(string message)
    {
        MessageBox.Show(message);
    }
}

public class MyClass
{
    private INotification notification;

    public MyClass(INotification notification)
    {
        this.notification = notification;
    }

    public void SomeFunction(int someValue)
    {
        // Replace with whatever your actual code is...
        ToDate toDate = new SomeOtherClass().SomeOtherFunction(someValue);
        CheckToDate(toDate);
    }

    private void CheckToDate(DateTime ToDate)
    {
        if (Manager.MaxToDate < ToDate.Year)
            notification.Show("toDate, too late!: " + toDate.ToString());
    }
}

Your unit test would make it's own custom INotification class, pass it to the constructor of MyClass, and invoke the SomeFunction method.

You'll probably want to abstract things like Manager, and the classes involve in computing ToDate in a similar way.


Introducing unit testing often makes you think more actively about code design (if you didn't already). Your case is interesting in this aspect. It is tricky to test, and one reason for that is that it does two different things:

  • It validates the date
  • It reacts on a failed validation by showing a message

A well-crafted method does only one thing. So, I would recommend refactoring the code a bit so that you get a validation method that does nothing but validates. This method will be very simple to test:

public bool IsDateValid(DateTime toDate)
{
    // just guessing on the rules here...
    return (Manager.MaxToDate >= toDate.Year);
}

This will also make the validation code far more reusable, since it moves the desicion on how to treat the result to the calling code.

0

精彩评论

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