Exception message in xunit includes parameter so my test fails - c#

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?

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);
}

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");
}

Related

Xunit to return a new argument

I am new to Xunit. I throw a new exception in my code :
throw new NotImplementedException("Missing UPC. File is corrupt");
in my test file I wrote:
[Fact]
public void ParserRow_Throws_Exception_For_Null_UPC()
{
var record = GetRessoRecord();
record.UPC = "";
RevenueReportItem result = Sut.ParseRow(record);
Assert.Throws<NotImplementedException>(() => Sut.ParseRow(record));
}
The error message in the Test Explorer says
System.NotImplementedException : Missing UPC. File is corrupt
How do I add in the returned quote?
The synchronous variant of Throws<> is now obsolete.
You're calling the tested method twice. Remove the first call that's outside of the assert, as it will throw the exception and halt test execution.
The call to Throws<> returns the exception object, from which you can retrieve and check the message, like so:
[Fact]
public async Task ParserRow_Throws_Exception_For_Null_UPC()
{
var exceptionDetails = await Assert.ThrowsAsync<NotImplementedException>(() => {
throw new NotImplementedException("foo");
});
Assert.Equal("foo", exceptionDetails.Message);
}

How to verify method was reached if throw before

I have my method as below which adds new user to database when such user name doesn't exist in database. If user name exist it throws custom exception therefore addUserType method is not reached.
public void CreateUserType(UserType userType)
{
if (userType == null) throw new ApplicationException("UserType object cannot be null");
if (_bottleClientQuery.IsUserTypeExist(userType.Name)) throw new ApplicationException("Such user type name already exist");
_bottleClientRepository.AddUserType(userType);
}
My testing methods are as shown below:
This method correctly giving me the expected result:
[Test]
public void CreateUserType_UserTypeExists_ThrowsApplicationException()
{
UserQuery.Setup(uow => uow.IsUserTypeExist(It.IsAny<string>())).Returns(true);
Assert.Throws<Exceptions.ApplicationException>(() => CreateClientService.CreateUserType(new UserType()));
}
Nevertheless in this method i want to check whether AddUserType was reached or not. I setup it as IsUserTypeExist returns true which means such user name exist therefore
AddUserType will be not reached.
[Test]
public void CreateUserType_UserTypeExists_AddUserTypeRepositoryNotReached()
{
UserQuery.Setup(uow => uow.IsUserTypeExist(It.IsAny<string>())).Returns(true);
CreateClientService.CreateUserType(new UserType());
UserRepository.Verify(uow => uow.AddUserType(It.IsAny<UserType>()),Times.Never);
}
The problem with second test method is the ApplicationException is thrown (that's fully fine and expected) but i would really like to test whether AddUserType was not reached.
Is it possible when before thrown exception was there?
You could change your test method to something like this;
[Test]
public void CreateUserType_UserTypeExists_AddUserTypeRepositoryNotReached()
{
UserQuery.Setup(uow => uow.IsUserTypeExist(It.IsAny<string>())).Returns(true);
Assert.Throws<ApplicationException>(() => CreateClientService.CreateUserType(new UserType()));
UserRepository.Verify(uow => uow.AddUserType(It.IsAny<UserType>()),Times.Never);
}
This will both verify that the expected exception is thrown and ensure that execution continues to your next verification.

What is the correct way to test this code using MOQ?

I have the following method:
public void MoveChannelUp(string channelName)
{
var liveChannels = _repository.GetChannels<LiveChannel>();
var channels = GetModifiedChannelsList(channelName, liveChannels);
_repository.SaveChannels(channels);
}
I want to set up an expectation on the SaveChannels() call so that the correct channels parameter is passed in.
I tried :
channelsRepository.Setup(x => x.SaveChannels(reorderedChannels));
where reorderedChannels is what I expect the GetModifiedChannelsList() call will return and but I got Mock verification exception (probably due to reorderedChannels is not the same object as channels???)
So it is GetModifiedChanneslsList() which I really want to test (I know I can use reflection to test this)
So how do I test the correct channels list is passed to SaveChannels()?
You can do something like this (I assume there is a type called Channel and the parameter for SaveChannels is List<Channel>; substitute with the actual):
var expectedChannels = new List<Channel> { new Channel() }; // set up expected channels here
var channelsRepo = new Mock<IChannelsRepository>();
// perform your unit test using channelsRepo here, for example:
channelsRepo.Object.SaveChannels(new List<Channel> { new Channel() });
channelsRepo.Verify(x => x.SaveChannels(It.Is<List<Channel>>(l => l.SequenceEqual(expectedChannels)))); // will throw an exception if call to SaveChannels wasn't made, or the List of Channels params did not match the expected.
What this code does is verify that the SaveChannels method is called at least once with the right list of channels. If that does not happen, Verify will throw an exception and your unit test will fail as expected.

Unit Testing contents of error message?

I have a class that raises an event with an error message.
In some of my tests I am subscribing to the event and asserting that the error message is not empty.
[Test]
public MyMethod_DoBad_ErrorMessageNotEmpty()
{
var logic = new MyClass();
string ErrorMessage = String.Empty;
logic.DisplayError += delegate(string s)
{
ErrorMessage = s;
};
logic.DoItBadly();
Assert.IsFalse(String.IsNullOrWhiteSpace(ErrorMessage));
}
//MyClass
public void DoItBadly()
{
//do something naughty but not final
DisplayError("Naughty");
//some other problem arises
if (1==1)
DisplayError("Something else naughty");
}
However I am starting to find in edge case testing that my new tests that should fail, pass because it has raised an error event previously in the code before it has got to where I want it to.
Therefore should I be asserting that the error message contains a specified string?
However I am starting to find in edge case testing that my new tests that should fail, pass because it has raised an error event previously in the code before it has got to where I want it to.
That suggests you're either reusing an existing object between tests, or that your test is doing too much. If you can't help but do work before the real operation you want to test, you can write your test as:
// Construct objects
// Do setup work
// Check that there's no error message yet
// Do work you expect to fail
// Check that there *is* an error message
Of course you can check for the exact error message, but that's likely to end up being time-consuming. If you're using reasonably ad-hoc error reporting (not worrying about i18n etc) then I'd personally just check whether an error message is present or not.
I think you should test both this cases in different tests:
[Test]
public ShouldRaiseNaughtyErrorWhenDoBadly()
{
var logic = new MyClass();
string errorMessage = String.Empty;
logic.DisplayError += delegate(string s) {errorMessage = s; };
logic.DoItBadly();
Assert.That(errorMessage, Is.EqualTo("Naughty"));
}
[Test]
public ShouldRaiseElseNaughtyErrorWhenDoBadlyWithOtherProblem()
{
var logic = new MyClass();
string errorMessage = String.Empty;
logic.DisplayError += delegate(string s) {errorMessage = s; };
// do something for other problem condition
logic.DoItBadly();
Assert.That(errorMessage, Is.EqualTo("Something else naughty"));
}
Or, if you need to check both errors where raised:
[Test]
public ShouldRaiseBothErrors()
{
var logic = new MyClass();
List<string> errorMessages = new List<string>();
logic.DisplayError += delegate(string s) {errorMessages.Add(s); };
// do something for other problem condition
logic.DoItBadly();
Assert.That(errorMessages.Count, Is.EqualTo(2));
Assert.That(errorMessages[0], Is.EqualTo("Naughty"));
Assert.That(errorMessages[1], Is.EqualTo("Something else naughty"));
}
UPDATE:
Considering event-based nature of your notifications, you can catch them all and then search for some concrete error:
[Test]
public ShouldRaiseNaughtyErrorWhenDoBadly()
{
var logic = new MyClass();
List<string> errorMessages = new List<string>();
logic.DisplayError += delegate(string s) { errorMessages.Add(s); };
logic.DoItBadly();
Assert.That(errorMessages.Contains("Naughty"));
}
Ideally, you would want to isolate and abstract the areas of DoItBadly() that are polluting your error text with the error message, so that you can test the rest of the method without problem.
However, given the understanding that it can often be easier said than done, the next best thing would be to only populate ErrorMessage with s if a certain condition is met (or have a whitelist of error messages that would not populate ErrorMessage). So if you only set ErrorMessage if it is not the error you have deemed "acceptable," then your test should pass and your own requirement should be met.
Though even better would be to assert a positive outcome, instead of making your success case the absence of negative outcomes.

How do I check "no exception occurred" in my MSTest unit test?

I'm writing a unit test for this one method which returns "void". I would like to have one case that the test passes when there is no exception thrown. How do I write that in C#?
Assert.IsTrue(????)
(My guess is this is how I should check, but what goes into "???")
I hope my question is clear enough.
Your unit test will fail anyway if an exception is thrown - you don't need to put in a special assert.
This is one of the few scenarios where you will see unit tests with no assertions at all - the test will implicitly fail if an exception is raised.
However, if you really did want to write an assertion for this - perhaps to be able to catch the exception and report "expected no exception but got this...", you can do this:
[Test]
public void TestNoExceptionIsThrownByMethodUnderTest()
{
var myObject = new MyObject();
try
{
myObject.MethodUnderTest();
}
catch (Exception ex)
{
Assert.Fail("Expected no exception, but got: " + ex.Message);
}
}
(the above is an example for NUnit, but the same holds true for MSTest)
In NUnit, you can use:
Assert.DoesNotThrow(<expression>);
to assert that your code does not throw an exception. Although the test would fail if an exception is thrown even if there was no Assert around it, the value of this approach is that you can then distinguish between unmet expectations and bugs in your tests, and you have the option of adding a custom message that will be displayed in your test output. A well-worded test output can help you locate errors in your code that have caused a test to fail.
I think it's valid to add tests to ensure that your code is not throwing exceptions; for example, imagine you are validating input and need to convert an incoming string to a long. There may be occasions when the string is null, and this is acceptable, so you want to ensure that the string conversion does not throw an exception. There will therefore be code to handle this occasion, and if you haven't written a test for it you will be missing coverage around an important piece of logic.
This helper class scratched my itch with MSTest. Maybe it can scratch yours also.
[TestMethod]
public void ScheduleItsIneligibilityJob_HasValid_CronSchedule()
{
// Arrange
var factory = new StdSchedulerFactory();
IScheduler scheduler = factory.GetScheduler();
// Assert
AssertEx.NoExceptionThrown<FormatException>(() =>
// Act
_service.ScheduleJob(scheduler)
);
}
public sealed class AssertEx
{
public static void NoExceptionThrown<T>(Action a) where T:Exception
{
try
{
a();
}
catch (T)
{
Assert.Fail("Expected no {0} to be thrown", typeof(T).Name);
}
}
}
Don't test that something doesn't happen. It's like assuring that code doesn't break. That's sort of implied, we all strive for non-breaking, bug-less code. You want to write tests for that? Why just one method? Don't you want all your methods being tested that they don't throw some exception? Following that road, you'll end up with one extra, dummy, assert-less test for every method in your code base. It brings no value.
Of course, if your requirement is to verify method does catch exceptions, you do test that (or reversing it a bit; test that it does not throw what it is supposed to catch).
However, the general approach/practices remain intact - you don't write tests for some artificial/vague requirements that are out of scope of tested code (and testing that "it works" or "doesn't throw" is usually an example of such - especially in scenario when method's responsibilities are well known).
To put it simple - focus on what your code has to do and test for that.
I like to see an Assert.Whatever at the end of each test, just for consistency... without one, can I really be sure there's not supposed to be one there?
For me, this is as simple as putting Assert.IsTrue(true);
I know I didn't accidentally put that code in there, and thus I should be confident enough at quick a skim through that this was as intended.
[TestMethod]
public void ProjectRejectsGappedVersioningByDefault() {
var files = new List<ScriptFile>();
files.Add(ScriptProjectTestMocks.GetVersion1to2());
files.Add(ScriptProjectTestMocks.GetVersion3to4());
Assert.Throws<ScriptProject.InvalidProjectFormatException>(() => {
var sut = new ScriptProject(files);
});
}
[TestMethod]
public void ProjectAcceptsGappedVersionsExplicitly() {
var files = new List<ScriptFile>();
files.Add(ScriptProjectTestMocks.GetVersion1to2());
files.Add(ScriptProjectTestMocks.GetVersion3to4());
var sut = new ScriptProject(files, true);
Assert.IsTrue(true); // Assert.Pass() would be nicer... build it in if you like
}
My friend Tim told me about ExpectedException. I really like this b/c it is more succinct, less code, and very explicit that you are testing for an exception.
[TestMethod()]
[ExpectedException(typeof(System.Exception))]
public void DivideTest()
{
int numerator = 4;
int denominator = 0;
int actual = numerator / denominator;
}
You can read way more about it here: ExpectedException Attribute Usage.
With Xunit you can use this:
var exception = Record.Exception(() =>
MethodUnderTest());
Assert.Null(exception);
or for async operations
var exception = await Record.ExceptionAsync(async () =>
await MethodUnderTestAsync());
Assert.Null(exception);
Another way which worked for me is to store it in a variable and check output.
var result = service.Run()
Assert.IsFalse(result.Errors.Any())
using Moq;
using Xunit;
[Fact]
public void UnitTest_DoesNotThrow_Exception()
{
var builder = new Mock<ISomething>().Object;
//Act
var exception = Record.Exception(() => builder.SomeMethod());
//Assert
Assert.Null(exception);
}

Categories