đ Lecture Summary (Quick Revision)
āĻāĻŦāĻŋāώā§āϝāϤ⧠āĻĻā§āϰā§āϤ āϰāĻŋāĻāĻŋāĻļāύ āĻĻā§āĻā§āĻžāϰ āĻāύā§āϝ āϏāĻŽā§āĻĒā§āϰā§āĻŖ āϞā§āĻāĻāĻžāϰā§āϰ āĻŽā§āϞ āĻāĻžāĻāĻā§āϞ⧠āύāĻŋāĻā§ āϞāĻŋāϏā§āĻ āĻāĻāĻžāϰ⧠āĻĻā§āĻā§āĻž āĻšāϞā§:
- Interface Update:
IPersonsServiceāĻāύā§āĻāĻžāϰāĻĢā§āϏā§GetPersonByPersonIdāύāĻžāĻŽā§āϰ āύāϤā§āύ āĻŽā§āĻĨāĻĄ āĻĄāĻŋāĻā§āϞā§ā§āĻžāϰ āĻāϰāĻž, āϝāĻž āĻāύāĻĒā§āĻ āĻšāĻŋāϏā§āĻŦā§Guid?āĻā§āϰāĻšāĻŖ āĻāϰ⧠āĻāĻŦāĻPersonResponse?āϰāĻŋāĻāĻžāϰā§āύ āĻāϰā§āĨ¤ - Dummy Implementation:
PersonsServiceāĻā§āϞāĻžāϏ⧠āĻŽā§āĻĨāĻĄāĻāĻŋāϰ āĻāύāĻŋāĻļāĻŋā§āĻžāϞ āĻŦāĻž āĻĄāĻžāĻŽāĻŋ āĻāĻŽāĻĒā§āϞāĻŋāĻŽā§āύā§āĻā§āĻļāύ āĻāϰāĻž (āϝā§āĻāĻžāύā§NotImplementedExceptionāĻĨā§āϰ⧠āĻāϰāĻž āĻšā§)āĨ¤ - Test Case 1 (Null ID):
GetPersonByPersonId_NullPersonIdāĻŽā§āĻĨāĻĄ āϤā§āϰāĻŋ āĻāϰ⧠āĻā§āĻ āĻāϰāĻž āϝā§, āĻāύāĻĒā§āĻnullāĻĻāĻŋāϞ⧠āĻŽā§āĻĨāĻĄāĻāĻŋnullāϰāĻŋāĻāĻžāϰā§āύ āĻāϰ⧠āĻāĻŋāύāĻž (UsingAssert.Null)āĨ¤ - Dependency Setup: Test āĻā§āϞāĻžāϏā§āϰ āĻāύāϏā§āĻā§āϰāĻžāĻā§āĻāϰā§
ICountriesServiceāĻāύāĻŋāĻļāĻŋā§āĻžāϞāĻžāĻāĻ āĻāϰāĻž, āĻāĻžāϰāĻŖ Person āĻ ā§āϝāĻžāĻĄ āĻāϰāĻžāϰ āĻāύā§āϝ āĻāĻāĻāĻŋ āĻā§āϝāĻžāϞāĻŋāĻĄCountryIdāĻĒā§āϰā§ā§āĻāύāĨ¤ - Test Case 2 (Valid ID):
GetPersonByPersonId_WithProperPersonIdāĻŽā§āĻĨāĻĄ āϤā§āϰāĻŋ āĻāϰāĻžāĨ¤ āĻāĻāĻžāύ⧠āĻĒā§āϰāĻĨāĻŽā§ āĻāĻāĻāĻŋ Country āĻ ā§āϝāĻžāĻĄ āĻāϰāĻž āĻšā§, āϤāĻžāϰāĻĒāϰ āϏā§āĻ āĻĻā§āĻļā§āϰ ID āĻĻāĻŋā§ā§ āĻāĻāĻāĻŋ Person āĻ ā§āϝāĻžāĻĄ āĻāϰāĻž āĻšā§āĨ¤ āĻļā§āώ⧠āϏā§āĻ Person-āĻāϰ ID āĻĻāĻŋā§ā§GetPersonByPersonIdāĻāϞ āĻāϰ⧠āĻā§āĻ āĻāϰāĻž āĻšā§ āĻĄā§āĻāĻž āĻ āĻŋāĻāĻŽāϤ⧠āĻāϏāĻā§ āĻāĻŋāύāĻž (UsingAssert.Equal)āĨ¤ - TDD Verification: āĻā§āϏā§āĻāĻā§āϞ⧠āϰāĻžāύ āĻāϰ⧠āĻĻā§āĻāĻž āϝ⧠āϏāĻŦāĻā§āϞ⧠āĻĢā§āĻāϞ āĻāϰāĻā§, āĻāĻžāϰāĻŖ āĻŽā§āĻĨāĻĄāĻāĻŋāϤ⧠āĻāĻāύ⧠āĻāϏāϞ āϞāĻāĻŋāĻ āϞā§āĻāĻž āĻšā§āύāĻŋāĨ¤
đ§ Comprehensive Breakdown
āĻāĻ āϞā§āĻāĻāĻžāϰ⧠āĻāĻŽāϰāĻž TDD (Test Driven Development) āĻ ā§āϝāĻžāĻĒā§āϰā§āĻ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰ⧠āĻĄāĻžāĻāĻžāĻŦā§āϏ (āĻŦāĻž āϞāĻŋāϏā§āĻ) āĻĨā§āĻā§ āύāĻŋāϰā§āĻĻāĻŋāώā§āĻ āĻāĻāĻāĻŋ ID āĻĻāĻŋā§ā§ Person āĻā§āĻāĻā§ āĻŦā§āϰ āĻāϰāĻžāϰ āϞāĻāĻŋāĻ āĻā§āϏā§āĻ āĻāϰāĻž āĻļāĻŋāĻāĻŦāĨ¤ āĻāϞā§āύ āĻĒā§āϰāϤāĻŋāĻāĻŋ āϧāĻžāĻĒ āĻŦāĻŋāϏā§āϤāĻžāϰāĻŋāϤāĻāĻžāĻŦā§ āĻŦā§āĻā§ āύāĻŋāĻāĨ¤
ā§§. Interface-āĻ Method āĻĄāĻŋāĻā§āϞā§āϝāĻŧāĻžāϰā§āĻļāύ (Priority: 7/10)
Concept & Why: āϝāĻāύ Controller āĻĨā§āĻā§ āĻā§āύ⧠āĻāĻāĻāĻŋ āύāĻŋāϰā§āĻĻāĻŋāώā§āĻ Person-āĻāϰ āĻĄāĻŋāĻā§āĻāϞāϏ āĻĻā§āĻāĻžāϰ āϰāĻŋāĻā§āϝāĻŧā§āϏā§āĻ āĻāϏāĻŦā§, āϤāĻāύ āĻāĻ āĻŽā§āĻĨāĻĄāĻāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰāĻž āĻšāĻŦā§āĨ¤ āϝāĻĻāĻŋ āĻāĻ ID-āĻāϰ āĻā§āύ⧠Person āύāĻž āĻĒāĻžāĻā§āĻž āϝāĻžā§ āĻŦāĻž ID null āĻšā§, āϤāĻŦā§ Exception āĻĨā§āϰ⧠āĻāϰāĻžāϰ āĻŦāĻĻāϞ⧠āĻŽā§āĻĨāĻĄāĻāĻŋ null āϰāĻŋāĻāĻžāϰā§āύ āĻāϰāĻŦā§āĨ¤ āϤāĻžāĻ āϰāĻŋāĻāĻžāϰā§āύ āĻāĻžāĻāĻĒ āĻāĻŦāĻ āĻāύāĻĒā§āĻ āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāĻāĻžāϰ āĻāĻā§āĻā§āĻ Nullable (?) āĻāϰāĻž āĻšā§ā§āĻā§āĨ¤
// ServiceContracts/IPersonsService.cs
/// <summary>
/// Returns the matching person object
/// </summary>
/// <param name="personId">Person ID to search</param>
/// <returns>Returns matching person object or null</returns>
PersonResponse? GetPersonByPersonId(Guid? personId);
⧍. Dummy Implementation in Service (Priority: 5/10)
Concept & Why: āĻāύā§āĻāĻžāϰāĻĢā§āϏ⧠āĻŽā§āĻĨāĻĄ āĻĄāĻŋāĻā§āϞā§ā§āĻžāϰ āĻāϰāĻžāϰ āĻĒāϰ Service āĻā§āϞāĻžāϏ⧠āϤāĻž āĻāĻŽāĻĒā§āϞāĻŋāĻŽā§āύā§āĻ āĻāϰāϤ⧠āĻšā§āĨ¤ TDD-āĻāϰ āύāĻŋā§āĻŽ āĻ āύā§āϝāĻžā§ā§ āĻā§āĻĄ āϞā§āĻāĻžāϰ āĻāĻā§āĻ āϤāĻžāϰ āĻā§āϏā§āĻ āϞāĻŋāĻāϤ⧠āĻšā§, āϤāĻžāĻ āĻāĻŽāϰāĻž āϏāĻžāĻŽā§āĻŋāĻāĻāĻžāĻŦā§ āĻāĻāĻāĻŋ āĻĄāĻžāĻŽāĻŋ āĻāĻā§āϏā§āĻĒāĻļāύ āĻĨā§āϰ⧠āĻāϰ⧠āϰāĻžāĻāĻāĻŋāĨ¤
(VS / VS Code Shortcut: IPersonsService-āĻāϰ āĻāĻĒāϰ āĻāĻžāϰā§āϏāϰ āϰā§āĻā§ Ctrl + . āĻāĻžāĻĒāϞ⧠âImplement Interfaceâ āĻ
āĻĒāĻļāύ āĻāϏāĻŦā§, āϝāĻž āϏā§āĻŦā§āĻāĻā§āϰāĻŋā§āĻāĻžāĻŦā§ āĻŽā§āĻĨāĻĄ āϤā§āϰāĻŋ āĻāϰ⧠āĻĻā§ā§āĨ¤)
// Services/PersonsService.cs
public PersonResponse? GetPersonByPersonId(Guid? personId)
{
throw new NotImplementedException();
}
ā§Š. Unit Test 1: Null PersonId āĻā§āĻ āĻāϰāĻž (Priority: 9/10)
Concept & Why: āĻāĻŽāĻžāĻĻā§āϰ āĻĒā§āϰāĻĨāĻŽ āĻā§āϏā§āĻ āĻā§āϏ āĻšāϞā§â āϝāĻĻāĻŋ āĻāĻāĻāĻžāϰ āĻā§āϞ āĻāϰ⧠null āĻā§āϝāĻžāϞ⧠āĻĒāĻžāĻ āĻžā§, āϤāĻŦā§ āϏāĻŋāϏā§āĻā§āĻŽ āĻā§āϰā§āϝāĻžāĻļ āύāĻž āĻāϰ⧠āϝā§āύ āϏā§āύā§āĻĻāϰāĻāĻžāĻŦā§ null āϰāĻŋāĻāĻžāϰā§āύ āĻāϰā§āĨ¤
#region GetPersonByPersonId
[Fact]
public void GetPersonByPersonId_NullPersonId()
{
// Arrange
Guid? personId = null;
// Act
PersonResponse? person_response_from_get = _personsService.GetPersonByPersonId(personId);
// Assert
Assert.Null(person_response_from_get);
}
#endregion
ā§Ē. Test Class-āĻ Dependency Setup (Priority: 8/10)
Concept & Why: āĻāĻāĻāĻŋ āĻā§āϝāĻžāϞāĻŋāĻĄ Person āϤā§āϰāĻŋ āĻāϰāϤ⧠āĻšāϞ⧠āϤāĻžāϰ CountryId (Foreign Key) āĻĻāϰāĻāĻžāϰāĨ¤ āϝā§āĻšā§āϤ⧠āĻĒā§āϰāϤāĻŋāĻāĻŋ āĻāĻāύāĻŋāĻ āĻā§āϏā§āĻ āϏāĻŽā§āĻĒā§āϰā§āĻŖ āϏā§āĻŦāĻžāϧā§āύ (Independent) āĻāĻŦāĻ āĻĄāĻžāĻāĻžāĻŦā§āϏ/āϞāĻŋāϏā§āĻ āĻĢāĻžāĻāĻāĻž āĻĨāĻžāĻā§, āϤāĻžāĻ āĻāĻŽāĻžāĻĻā§āϰ āĻā§āϏā§āĻā§āϰ āĻā§āϤāϰā§āĻ āĻāĻā§ āĻāĻāĻāĻŋ Country āϤā§āϰāĻŋ āĻāϰ⧠āύāĻŋāϤ⧠āĻšāĻŦā§āĨ¤ āĻāϰ āĻāύā§āϝ PersonsServiceTest āĻā§āϞāĻžāϏā§āϰ āĻāύāϏā§āĻā§āϰāĻžāĻā§āĻāϰ⧠CountriesService āĻāύāĻā§āĻā§āĻ āĻāϰāϤ⧠āĻšāĻŦā§āĨ¤
public class PersonsServiceTest
{
private readonly IPersonsService _personsService;
private readonly ICountriesService _countriesService;
public PersonsServiceTest()
{
_personsService = new PersonsService();
_countriesService = new CountriesService();
}
}
ā§Ģ. Unit Test 2: Valid PersonId āĻĻāĻŋāϝāĻŧā§ āĻĄā§āĻāĻž āϰāĻŋāĻā§āϰāĻŋāĻ āĻāϰāĻž (Priority: 10/10)
Concept & Why: āĻāĻāĻŋ āĻāĻ āϞā§āĻāĻāĻžāϰā§āϰ āϏāĻŦāĻā§ā§ā§ āĻā§āϰā§āϤā§āĻŦāĻĒā§āϰā§āĻŖ āĻ āĻāĻļāĨ¤ xUnit-āĻ Test Isolation-āĻāϰ āĻāĻžāϰāĻŖā§ āĻāĻŽāĻžāĻĻā§āϰ āĻĒā§āϰāĻĨāĻŽā§ āĻāĻāĻāĻŋ Country āĻ ā§āϝāĻžāĻĄ āĻāϰ⧠āϤāĻžāϰ ID āύāĻŋāϤ⧠āĻšāĻŦā§, āϤāĻžāϰāĻĒāϰ āϏā§āĻ ID āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰ⧠āĻāĻāĻāĻŋ Person āĻ ā§āϝāĻžāĻĄ (Arrange) āĻāϰ⧠āύāĻŋāϤ⧠āĻšāĻŦā§āĨ¤ āĻāϰāĻĒāϰ āĻā§āύāĻžāϰā§āĻ āĻšāĻā§āĻž Person ID āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰ⧠āĻĄā§āĻāĻž āϤā§āϞ⧠āĻāύāϤ⧠(Act) āĻšāĻŦā§āĨ¤ āĻāĻŦāĻ āϏāĻŦāĻļā§āώ⧠Expected āĻāĻŦāĻ Actual āĻĄā§āĻāĻž āĻŽā§āϞāĻžāϤ⧠(Assert) āĻšāĻŦā§āĨ¤
[Fact]
public void GetPersonByPersonId_WithProperPersonId()
{
// Arrange 1: Country āϤā§āϰāĻŋ āĻāϰāĻž
CountryAddRequest country_request = new CountryAddRequest() { CountryName = "Canada" };
CountryResponse country_response = _countriesService.AddCountry(country_request);
// Arrange 2: Person āϤā§āϰāĻŋ āĻāϰāĻž (āĻāĻĒāϰā§āϰ CountryId āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰā§)
PersonAddRequest person_request = new PersonAddRequest()
{
PersonName = "John",
Email = "john@example.com",
Address = "123 Street",
CountryId = country_response.CountryId,
Gender = GenderOptions.Male,
DateOfBirth = DateTime.Parse("2000-01-01"),
ReceiveNewsLetters = false
};
PersonResponse person_response_from_add = _personsService.AddPerson(person_request);
// Act: āĻ
ā§āϝāĻžāĻĄ āĻāϰāĻž Person āĻāϰ ID āĻĻāĻŋā§ā§ āĻĄā§āĻāĻž āϰāĻŋāĻā§āϰāĻŋāĻ āĻāϰāĻž
PersonResponse? person_response_from_get = _personsService.GetPersonByPersonId(person_response_from_add.PersonId);
// Assert: āĻ
ā§āϝāĻžāĻĄ āĻāϰāĻž āĻĄā§āĻāĻž (Expected) āĻāĻŦāĻ āϰāĻŋāĻā§āϰāĻŋāĻ āĻāϰāĻž āĻĄā§āĻāĻž (Actual) āĻšā§āĻŦāĻšā§ āĻāĻ āĻāĻŋāύāĻž āϤāĻž āĻā§āĻ āĻāϰāĻž
Assert.Equal(person_response_from_add, person_response_from_get);
}
āύā§āĻ: Assert.Equal āĻāĻāĻžāύ⧠āϏāĻ āĻŋāĻāĻāĻžāĻŦā§ āĻāĻžāĻ āĻāϰāĻŦā§ āĻāĻžāϰāĻŖ āĻāĻā§āϰ āϞā§āĻāĻāĻžāϰāĻā§āϞā§āϤ⧠āĻāĻŽāϰāĻž PersonResponse āĻā§āϞāĻžāϏ⧠Equals āĻŽā§āĻĨāĻĄāĻāĻŋ Override āĻāϰ⧠āĻāϏā§āĻāĻŋāϞāĻžāĻŽ, āϝāĻž Value Equality āĻā§āĻ āĻāϰā§āĨ¤
đ Modern C# (.NET 10) Updates & Smarter Approach
āϞā§āĻāĻāĻžāϰā§āϰ āĻā§āĻĄāĻāĻŋ āϏāĻ āĻŋāĻ, āϤāĻŦā§ āĻāϧā§āύāĻŋāĻ C# (C# 9 - 12) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰ⧠āĻā§āϏā§āĻāĻŋāĻ āĻāĻŦāĻ āĻ āĻŦāĻā§āĻā§āĻ āϤā§āϰāĻŋ āĻāϰāĻ āϏā§āĻŽāĻžāϰā§āĻ āĻ āĻā§āϞāĻŋāύ āĻāϰāĻž āϝāĻžā§āĨ¤
1. Target-typed new Expressions:
āĻ
āĻŦāĻā§āĻā§āĻ āϤā§āϰāĻŋ āĻāϰāĻžāϰ āϏāĻŽā§ āĻā§āϞāĻžāϏā§āϰ āύāĻžāĻŽ āĻŦāĻžāϰāĻŦāĻžāϰ āύāĻž āϞāĻŋāĻā§ āĻļā§āϧ⧠new() āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻāϰāĻž āϝāĻžā§āĨ¤
// Old
CountryAddRequest country_request = new CountryAddRequest() { CountryName = "Canada" };
// Modern (C# 9+)
CountryAddRequest country_request = new() { CountryName = "Canada" };
2. Assert.Equivalent (xUnit Update):
āĻāĻā§āϰ āϞā§āĻāĻāĻžāϰāĻā§āϞā§āϤ⧠āĻāĻŽāĻžāĻĻā§āϰ āĻŽā§āϝāĻžāύā§ā§āĻžāϞāĻŋ Equals āĻŽā§āĻĨāĻĄ āĻāĻāĻžāϰāϰāĻžāĻāĻĄ āĻāϰāϤ⧠āĻšā§ā§āĻāĻŋāϞāĨ¤ āĻāĻŋāύā§āϤ⧠xUnit-āĻāϰ āĻāϧā§āύāĻŋāĻ āĻāĻžāϰā§āϏāύāĻā§āϞā§āϤ⧠Assert.Equivalent() āĻŽā§āĻĨāĻĄ āĻāύāĻž āĻšā§ā§āĻā§āĨ¤ āĻāĻĒāύāĻŋ āϝāĻĻāĻŋ Equals āĻŽā§āĻĨāĻĄ āĻāĻāĻžāϰāϰāĻžāĻāĻĄ āύāĻžāĻ āĻāϰā§āύ, āϤāĻŦā§āĻ Assert.Equivalent() āĻĻā§āĻāĻŋ āĻ
āĻŦāĻā§āĻā§āĻā§āϰ āĻā§āϤāϰā§āϰ āĻĒā§āϰāĻĒāĻžāϰā§āĻāĻŋāĻā§āϞā§āϰ āĻā§āϝāĻžāϞ⧠(Value Equality) āĻā§āĻ āĻāϰāϤ⧠āĻĒāĻžāϰā§!
// Modern way to check object equivalence without needing custom Equals() override
Assert.Equivalent(person_response_from_add, person_response_from_get);
đ Best Practices (For xUnit & Service Testing)
- Test Independence: āĻāĻāĻāĻŋ āĻāĻāύāĻŋāĻ āĻā§āϏā§āĻāĻā§ āĻāĻāύā§āĻ āĻ āύā§āϝ āĻāĻāĻāĻŋ āĻāĻāύāĻŋāĻ āĻā§āϏā§āĻā§āϰ āĻāĻĒāϰ āύāĻŋāϰā§āĻāϰāĻļā§āϞ āĻāϰāĻž āϝāĻžāĻŦā§ āύāĻžāĨ¤ āĻā§āϏā§āĻ āϰāĻžāύ āĻšāĻā§āĻžāϰ āĻāĻā§ āĻĄāĻžāĻāĻžāĻŦā§āϏ/āĻĄāĻžāĻāĻž āϏā§āĻā§āϰ āĻĄāĻŋāĻĢāϞā§āĻ āĻ āĻŦāϏā§āĻĨāĻžā§ (Empty) āĻāĻā§ āϧāϰ⧠āύāĻŋā§ā§ āŽ¤ā¯āŽĩā¯āŽ¯āŽžāŽŠ āĻĄā§āĻāĻž āĻā§āϏā§āĻā§āϰ āĻā§āϤāϰā§āĻ (Arrange āĻŦā§āϞāĻā§) āϤā§āϰāĻŋ āĻāϰ⧠āύāĻŋāϤ⧠āĻšāĻŦā§ (āϝā§āĻŽāύāĻāĻŋ āϞā§āĻāĻāĻžāϰ⧠Country āĻ ā§āϝāĻžāĻĄ āĻāϰ⧠āĻāϰāĻž āĻšā§ā§āĻā§)āĨ¤
- AAA Pattern: āĻā§āϏā§āĻā§āϰ āĻā§āϤāϰ⧠āĻāĻŽā§āύā§āĻ āĻāϰā§
// Arrange,// Act,// AssertāĻŦā§āϞāĻāĻā§āϞ⧠āĻāϞāĻžāĻĻāĻž āĻāϰ⧠āϰāĻžāĻāĻž āĻāĻāĻŋāϤāĨ¤ āĻāϤ⧠āĻ āύā§āϝ āĻĄā§āĻā§āϞāĻĒāĻžāϰāϰāĻž āĻā§āĻĄ āĻĻā§āĻāϞā§āĻ āĻŦā§āĻāϤ⧠āĻĒāĻžāϰ⧠āĻā§āϏā§āĻā§āϰ āĻĢā§āϞ⧠āĻā§āĻŽāύāĨ¤ - Meaningful Naming: āĻā§āϏā§āĻ āĻŽā§āĻĨāĻĄā§āϰ āύāĻžāĻŽ āĻĻā§āĻā§āĻ āĻāϰ āĻāĻĻā§āĻĻā§āĻļā§āϝ āĻŦā§āĻāĻž āĻāĻāĻŋāϤāĨ¤ āϝā§āĻŽāύ:
GetPersonByPersonId_WithProperPersonId_ReturnsPersonResponseāĨ¤