Reputation: 541
I am trying to check that an exception that I throw gives the correct error message.
I have a method in a class that withdraws (substracts) from a value. If the value is less than 0, I throw an exception.
if (amount < 0)
{
throw new System.ArgumentOutOfRangeException("amount", AmountLessThanZeroMessage);
}
This is my error message:
public const string AmountLessThanZeroMessage = "Amount is less than zero";
However, when I try to write a unit test to see if I get the correct message, it fails because of the parameter. This is the test:
[Fact]
public void CannotWithdrawLessThanZero()
{
// Arrange
var account = new Account("User", 23);
// Act
account.DepositCash(100);
var thrownException = Assert.Throws<ArgumentOutOfRangeException>(() => account.WithdrawCash(-10));
// Assert
Assert.Equal(Account.AmountLessThanZeroMessage, thrownException.Message);
}
The result includes the parameter in the end, resulting in a failed test:
It seems the actual message includes which parameter it refers to. How do I correct this message? Should I just add the line (Parameter 'amount')
to the expected string, or are there any better options?
Upvotes: 1
Views: 1916
Reputation: 32445
Messages changes often, message can be localized, so testing message equality will make tests more fragile.
Instead you can test that correct exception type was thrown, or even better and create domain specific exception to be sure that exception have been thrown for correct reasons.
public class WithdrawOfNegativeAmountNotAllowedException : Exception
{
public WithdrawOfNegativeAmountNotAllowedException(int amount)
: base($"Amount is less than zero ({amount})")
{
}
}
[Fact]
public void Cannot_withdraw_less_than_zero()
{
var account = new Account("User", 23);
account.DepositCash(100);
Action withdraw = () => account.WithdrawCash(-10);
withdraw.Should().Throw<WithdrawOfNegativeAmountNotAllowedException>();
}
In case you still want to test for correct message. Assertions of strings can be done in multiple ways, I would suggest to make assertion as loose as possible.
Test for part of the message which unlikely will change. For example test that message starts with some text or contain critical value.
[Fact]
public void Cannot_withdraw_less_than_zero()
{
var account = new Account("User", 23);
account.DepositCash(100);
Action withdraw = () => account.WithdrawCash(-10);
withdraw.Should()
.Throw<WithdrawOfNegativeAmountNotAllowedException>()
.And
.Message.Should().ContainEquivalentOf("Amount is less than zero");
}
Upvotes: 2
Reputation: 2264
You can create the exact same exception and use its message property. Like the code below
[Fact]
public void CannotWithdrawLessThanZero()
{
// Arrange
var account = new Account("User", 23);
var expectedException = new System.ArgumentOutOfRangeException("amount", AmountLessThanZeroMessage);
// Act
account.DepositCash(100);
var thrownException = Assert.Throws<ArgumentOutOfRangeException>(() => account.WithdrawCash(-10));
// Assert
Assert.Equal(expectedException.Message, thrownException.Message);
}
Upvotes: 5