haosmark
haosmark

Reputation: 1127

mstest how do I create test methods that work on the same object (cumulatively)?

I'm currently trying to implement testing for a calculator that I'm building (using composite pattern.) The first method should add $75, which works fine, however when the second method runs, "service" is reset and has $0 as job cost. If I combine both methods into one, then everything works as I expect it to. How do I retain value in service field?

[TestClass()]
public class JobTests
{
    private Service service;
    private LaborTime laborTime;
    private LaborRates laborRates;

    [TestInitialize]
    public void init()
    {
        service = new EmergencyService();
    }

    [TestMethod()]
    // add one hour of service at $75/50 rate
    public void Job_OnFullCost_Is75()
    {
        // Arrange
        laborTime = new LaborTime(
            checkIn: new DateTime(year: 2016, month: 7, day: 20, hour: 10, minute: 0, second: 0),
            checkOut: new DateTime(year: 2016, month: 7, day: 20, hour: 11, minute: 0, second: 0)
        );
        laborRates = new LaborRates(75, 50);
        service = new Labor(service, laborTime, laborRates);

        // Act
        var expected = 75.0M;
        var actual = service.JobCost;

        // Assert
        Assert.AreEqual(expected, actual);
    }

    [TestMethod()]
    // add another hour to the service, at same rate of $175/60
    public void Job_OnFullCost_Is125()
    {
        // Arrange
        laborTime = new LaborTime(
            checkIn: new DateTime(year: 2016, month: 7, day: 20, hour: 12, minute: 0, second: 0),
            checkOut: new DateTime(year: 2016, month: 7, day: 20, hour: 13, minute: 0, second: 0)
        );
        LaborRates laborRates = new LaborRates(75, 50);
        service = new Labor(service, laborTime, laborRates);
        //service.IsContinuation = true;

        // Act
        var expected = 125.0M;
        var actual = service.JobCost;

        // Assert
        Assert.AreEqual(expected, actual);
    }
}

Upvotes: 2

Views: 377

Answers (1)

StriplingWarrior
StriplingWarrior

Reputation: 156524

Since unit tests are not guaranteed to all be run together, or to be run in a specific order, it's best practice to avoid adding a temporal dependency between them (i.e. requiring them to be run in a specific order in order to work right).

Instead, extract out the "hard parts" of adding labor into a separate helper method, and make your two methods test specific scenarios.

[TestClass()]
public class JobTests
{
    private Service service;

    [TestInitialize]
    public void init()
    {
        service = new EmergencyService();
    }

    [TestMethod()]
    // add one hour of service at $75/50 rate
    public void Job_OnFullCost_Is75()
    {
        // Arrange
        AddHourOfService(75, 50);

        // Act
        var expected = 75.0M;
        var actual = service.JobCost;

        // Assert
        Assert.AreEqual(expected, actual);
    }

    [TestMethod()]
    // add another hour to the service, at same rate of $175/60
    public void Job_OnFullCost_Is125()
    {
        // Arrange
        AddHourOfService(75, 50);
        AddHourOfService(75, 50);

        // Act
        var expected = 125.0M;
        var actual = service.JobCost;

        // Assert
        Assert.AreEqual(expected, actual);
    }

    private void AddHourOfService(int cost, int time)
    {
        var laborTime = new LaborTime(
        checkIn: new DateTime(year: 2016, month: 7, day: 20, hour: 10, minute: 0, second: 0),
        checkOut: new DateTime(year: 2016, month: 7, day: 20, hour: 11, minute: 0, second: 0)
    );
        var laborRates = new LaborRates(75, 50);
        service = new Labor(service, laborTime, laborRates);
    }
}

Besides removing temporal coupling between tests, this also has the nice side-effect of making the tests' purpose obvious straight from the code. You no longer need comments like "add one hour of service at $75/50 rate" because AddHourOfService(75, 50) makes it pretty obvious that's what happens. Letting code self-document this way is good because it's too easy for comments to get out of sync with your code (as you can see by your second comment which says "$175/60" when that's clearly not what the test is doing).

Upvotes: 4

Related Questions