Unit test not reaching a piece of code - c#

I am new to unit testing. I want to do something as follows:
[Test]
[ExpectedException(ExceptionType = typeof(Exception))]
public void TestDeleteCategoryAssociatedToTest()
{
Category category = CategoryHelper.Create("category", Project1);
User user;
Test test1 = IssueHelper.Create(Project1, "summary1", "description1", user);
test1.Category = category;
category.Delete(user);
Assert.IsNotNull(Category.Load(category.ID));
Assert.IsNotNull(Test.Load(test1.ID).Category);
}
My aim here is to test that the category wasn't deleted by doing the Assert.IsNotNull()... but since it's throwing the exception, it's not reaching that piece of code. Any idea how I can improve on the above test?
Actually in my API I throw an exception in case the category is associated to a Test...
My snippet is:
IList<Test> tests= Test.LoadForCategory(this);
if (tests.Count > 0)
{
throw new Exception("Category '" + this.Name + "' could not be deleted because it has items assigned to it.");
}
else
{
base.Delete();
foreach (Test test in tests)
{
test.Category = null;
}
}

Try and test only one functionality per test. IOW write separate success and failure tests.

You could do something like:
[Test]
public void TestDeleteCategoryAssociatedToTest()
{
// Arrange
Category category = CategoryHelper.Create("category", Project1);
User user;
Test test1 = IssueHelper.Create(Project1, "summary1", "description1", user);
test1.Category = category;
try
{
// Act
category.Delete(user);
// Assert
Assert.Fail("The Delete method did not throw an exception.");
}
catch
{
Assert.IsNotNull(Category.Load(category.ID));
Assert.IsNotNull(Test.Load(test1.ID).Category);
}
}
The Assert.Fail() tells, that the Unit Test shall fail, if no exception has been thrown.
In case of an exception you can do other checks, like shown above.

Related

Exception message in xunit includes parameter so my test fails

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

Unit testing a method which Posts and Deletes by selection C#

I have this method which selects one row of a table and copies it to the another table and then it just deletes it from the first one. The problem is that how can I unit test it because it executes two methods (Post and Delete) at the same time.
Here is the method:
public ActionResult Pay(int id, Car car)
{
try
{
using (BookCarDBEntities db = new BookCarDBEntities())
{
var carToDelete = db.Cars.FirstOrDefault(c => c.Id == id);
var book = CreateNewBooking(carToDelete);
db.Bookings.Add(book);
db.Cars.Remove(carToDelete);
db.SaveChanges();
return RedirectToAction("ListBookings", "Home");
}
}
catch (Exception ex)
{
return View(ex + "error");
}
}
private Booking CreateNewBooking(Car car)
{
var bookingCreated = new Booking
{
id = car.Id,
model = car.model,
make = car.make,
price = car.price,
location = car.location
};
return bookingCreated;
}
It does not matter how many external function calls your method to have. To get the values from the method calls in your method (that you wanna unit test), you should mock those methods to return certain responses when called with certain parameters.
You should check Moq to learn how to mock methods. Also, I suggest you wrap your methods with an interface (BookCarDBEntities). It is more important to write code to testable rather than writing tests to unmanageable code. Hope this helps.

Integration test good practice

I am using unit test for some time. But now I need to write integration test. I should save something to DB, and than check if saved data is ok.
I can't find simple clean example of integration test.
My idea is to do like this:
[Test]
public void IntegrationTestExample()
{
// Arrange without mocking
var objec1 = new objec1();
var objec2 = new objec2();
var startTestClass = new startTestClass(objec1, objec2);
var saveData = "test data";
//Act
startTestClass.SaveToDB(saveData);
// Assert
var valueFromDB = SelectSavedData();
Assert.AreEqual(saveData, valueFromDB);
}
//Get data from DB for assert
private string SelectSavedData()
{
var sqlQuery = "Select TOP 1 data from table1";
var data = RepositoryForTest.selectSavedData(sqlQuery);
return data;
}
But I am not sure if this is good approach?
Do you have any suggestions?
You should test one piece of integration at a time, and leave the DB in such a state that it can run multiple tests and they should not affect each other. The MS test framework allows you to define methods that will setup and cleanup the tests at certain times during the test run, as shown in the following code
[TestClass]
public class ReturnServiceTest {
[ClassInitialize()]
public static void ClassInit(TestContext context) {
//INIT TEST DATA FOR THE CLASS
}
[TestCleanup]
public void TestCleanup() {
//CLEANUP DATA AFTER EACH TEST
}
[TestMethod]
public void IntegrationTestExample() {
// Arrange without mocking
var objec1 = new objec1();
var objec2 = new objec2();
var startTestClass = new startTestClass(objec1, objec2);
var saveData = "test data";
//Act
startTestClass.SaveToDB(saveData);
// Assert
var valueFromDB = SelectSavedData();
Assert.AreEqual(saveData, valueFromDB);
}
//Get data from DB for assert
private string SelectSavedData() {
var sqlQuery = "Select TOP 1 data from table1";
var data = RepositoryForTest.selectSavedData(sqlQuery);
return data;
}
}
When you are using a real database you should clean it up after each test attempt.
How you access the data is also not very clear, what proves, that RepositoryForTest does not contain any errors? Try to do the simplest thing that is possible for your assertion.
My Tests against databases look like:
public void Test()
{
try
{
// Fixture setup
// Create and insert data into Database (with plain ADO code)
// Exercise system
// Verify outcome
// use your data layer here
}
finally
{
// Teardown - other methods to clean the tables afterwards
DatabaseHelper.ClearLookups();
DatabaseHelper.ClearBeds();
...
}
}

Unit test void method with NSubstitute

I want to test if the Update or Insert function is called with a unit test. What would the unit test look like for this?
public void LogicForUpdatingAndInsertingCountriesFromMPtoClientApp()
{
var allCountriesAlreadyInsertedIntoClientDatabase = _countryBLL.GetAllCountries();
var countiresFromMP = GetAllCountriesWithTranslations();
List<Country> countiresFromMPmapped = new List<Country>();
foreach (var country in countiresFromMP)
{
Country newCountry = new Country();
newCountry.CountryCode = country.Code;
newCountry.Name = country.TranslatedText;
countiresFromMPmapped.Add(newCountry);
}
foreach (var country in countiresFromMPmapped)
{
//check if the country is already inserted into the Client Database,
//if it is update, else insert it
Country testedCountry = allCountriesAlreadyInsertedIntoClientDatabase
.Where(x => x.CountryCode == country.CountryCode)
.FirstOrDefault();
//here fallback function for tested country
if (testedCountry != null)
{
var countryToUpdate = _countryBLL.GetCountryByCode(testedCountry.CountryCode);
//return _countryBLL.UpdateCountry(countryToUpdate);
_countryBLL.UpdateCountry(countryToUpdate);
}
else
{
country.CountryId = Guid.NewGuid();
// return _countryBLL.InsertCountryFromMP(country);
_countryBLL.InsertCountryFromMP(country);
}
}
return null;
}
The method is wrapped in an interface which I can mock.
Are you trying to test for a specific call, or are you happy with just testing either call was received?
For the latter, you can use the ReceivedCalls() extension method to get a list of all the calls a substitute has received:
var allCalls = _countryBLL.ReceivedCalls();
// Assert “allCalls” contains “UpdateCountry” and “InsertCountry”
NSubstitute wasn’t really designed to support this, so it is quite messy.
To test a specific call was made, we can use Received():
_countryBLL.Received().UpdateCountry(Arg.Any<Country>());
// or require a specific country:
_countryBLL.Received().UpdateCountry(Arg.Is<Country>(x => x.CountryCode == expectedCountry));
This requires that the required dependencies have been substituted in for the test, which often results in tests like this:
[Test]
public void TestCountryIsUpdatedWhen….() {
var countryBLL = Substitute.For<ICountryBLL>();
// setup specific countries to return:
countryBLL.GetAllCountries().Returns( someFixedListOfCountries );
var subject = new MyClassBeingTested(countryBLL);
subject.LogicForUpdatingAndInsertingCountries…();
countryBLL.Received().UpdateCountry(…);
}

Correct work with exceptions

For example, I have the following method:
public void MeetingNoteSave(int MeetingID, string note, bool IsInviter, string Username)
{
meeting = Get<Meeting>(p => p.MeetingID == MeetingID && p.UserInviter.aspnet_User.UserName == Username);
MeetingNoteSaveCheckings(meeting, MeetingID);
// some actions here
}
void MeetingNoteSaveCheckings(Meeting meeting, int MeetingID)
{
DateTime currentDateWithTime = DateTime.Now;
if (meeting == null)
{
throw new Exception("Meeting does not exist. MeetingID=" + MeetingID);
}
DateTime meetingTime = meeting.MeetingTime.Day.AddHours(meeting.MeetingTime.Hour).AddMinutes(meeting.MeetingTime.Minute);
if (meetingTime > currentDateWithTime)
{
throw new Exception("Meeting is future. MeetingID=" + MeetingID + ". Meeting time = '" + meetingTime + "', Current time='" + currentDateWithTime + "'");
}
}
so, method can throw 2 exceptions - when meeting not exists with such parameters at all or when time of meeting more than current time (should be past or current).
Now, I'm writting Unit Tests. Simple method:
[TestMethod]
public void MeetingNoteSave()
{
_repository.MeetingNoteSave(1, "My note", true, "xxx#xxx.com");
}
Of course, call unit test will be fail with some parameters. I want to catch these cases, so, test should be success. I can do by 2 ways. First is simple, but a little dirty:
try
{
_repository.MeetingNoteSave(1, "My note", true, "xxx#xxx.com");
}
catch(Exception ex)
{
if (ex.Message.IndexOf("Meeting does not exist")>=0)
{
// some actions
}
if (ex.Message.IndexOf("Meeting is future")>=0)
{
// some actions
}
}
so, test will be success with incorrect input parameters (so, unit test can be used to test method with incorrect parameters), but fail with encountered errors. Good.
Other way - create special dummy exceptions like MeetingNullException and MeetingFutureException
public class MeetingNullException : Exception
{
}
public class MeetingFutureException : Exception
{
}
throw them and catch them. More correctly, but much more code. Dummy code.
Which way is more correctly?
Neither, they're both flawed. Your second approach is in the right direction though: you should avoid throwing general exceptions of type Exception; specific subclasses are much more expressive.
What you have to do in your tests then is use the [ExpectedException] attribute which will make them look like this:
[TestMethod]
[ExpectedException(typeof(MeetingNullException))]
public void MeetingNoteSave_WithNotExistingMeeting()
{
_repository.MeetingNoteSave(1, "My note", true, "xxx#xxx.com");
}
[TestMethod]
[ExpectedException(typeof(MeetingFutureException ))]
public void MeetingNoteSave_WithFutureDate()
{
_repository.MeetingNoteSave(1, "My note", true, "xxx#xxx.com");
}
Make sure you only have one test for each possible flow of your program: 2 exceptions means 2 tests. Personally I might avoid creating the specific subclasses and just use ArgumentException but that's up to you to decide. If you have expressive test names and the code is sufficiently self documenting, you'll know what argument is being referred to anyway.

Categories