Constructor Tests
Example 1 - Throws Exception
Test Case:
# | Test Case | Test Data | Expected |
---|---|---|---|
1 | Exception when name is null | Parameter name: null, Parameter amountOfMoney: 500 | ArgumentNullException, Parameter: name, Message: The argument must reference a string object. |
Testing Goal: The goal of this test is to cause the expected exception to be thrown and the exception should have the expected state.
[TestMethod]
public void Constructor_NameIsNull_ThrowsException()
{
// Arrange
string name = null;
decimal amountOfMoney = 500;
// Act & Assert
ArgumentNullException exception = Assert.ThrowsException<ArgumentNullException>(() => new Person(name, amountOfMoney));
// Assert exception state
Assert.AreEqual("name", exception.ParamName);
Assert.AreEqual("The argument must reference a string object.", GetExceptionMessage(exception.Message));
}
Notes:
- The act part of the test is
new Person(name, amountOfMoney)
. - The
Assert.ThrowsException<T>()
method generates aAssertFailedException
if the type of exception is not thrown. If the type of exception is thrown, a reference to the exception object is returned. - The assert part of the test includes verifying the parameter and message of the exception.
Non-Test Methods
Unit test class can also contain methods that aren't unit test methods. These methods do not have the TestMethod
attribute.
The test above calls a method called GetExceptionMessage
which extracts only the message from the Exception's Message property.
/// <summary>
/// Helper method to obtain only the message from an Exception object.
/// </summary>
/// <param name="exceptionMessage">The Exception's Message state.</param>
/// <returns>The Exception's message with the parameter omitted.</returns>
/// <remarks>
/// The Exception.Message property returns the Exception's message on line 1 and
/// the parameter name on line 2. This method reads the first line and returns
/// the message.
/// </remarks>
private string GetExceptionMessage(string exceptionMessage)
{
return new System.IO.StringReader(exceptionMessage).ReadLine();
}
Feel free to include this method in your test class.
Example 2 - Initialize State
Test Case:
# | Test Case | Test Data | Expected |
---|---|---|---|
6 | Initialize the amount of money to positive value | Parameter name: "Kenny", Parameter amountOfMoney: 500 | 500 |
Testing Goal: The goal of this test is to initialize the state of the object and verify it did so correctly.
[TestMethod]
public void Constructor_ValidAmountOfMoney_InitializeState()
{
// Arrange
string name = "Kenny";
decimal amountOfMoney = 500;
// Act
Person person = new Person(name, amountOfMoney);
// Reflection
PrivateObject target = new PrivateObject(person);
// Obtain object state
decimal actual = (decimal)target.GetField("amountOfMoney");
// Assert
Assert.AreEqual(amountOfMoney, actual);
}
Notes:
- Because the state is stored in a private field, the
PrivateObject
class is used to gain access to the class' private member. - The
GetField()
method returns anobject
type. Because fields can be any type, the method uses this polymorphic reference to the value of the field. A type cast is necessary to store the value in thedecimal
variable.
Testing in Isolation
One of the key parts to writing good unit tests is to test the unit in isolation. This means not directly invoking any other class members during the test. Testing in isolation ensures that no other part of the class is affecting the results of your test.
The following example, is a revised version of the test code from above. This example does not test the constructor method in isolation, as the get
accessor or the Name
property is invoked later in the test method.
[TestMethod]
public void Constructor_ValidAmountOfMoney_InitializeState()
{
// Arrange
string name = "Kenny";
decimal amountOfMoney = 500;
// Act
Person person = new Person(name, amountOfMoney);
// Obtain object state
decimal actual = person.AmountOfMoney;
// Assert
Assert.AreEqual(amountOfMoney, actual);
}
Notes:
- This test does not test the unit in isolation.