-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor and enhance test code for clarity and consistency #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8276678
3e69eb3
ded9e5f
1825691
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,6 @@ | |
| using CsvHelper.Configuration; | ||
| using CsvHelper.TypeConversion; | ||
|
|
||
| using FakeItEasy; | ||
|
|
||
| using RoyAppMaui.Models; | ||
|
|
||
| namespace RoyAppMaui.Converters.Tests; | ||
|
|
@@ -70,8 +68,12 @@ public void ConvertFromString_InvalidFormat_ThrowsFormatException() | |
| var context = new CsvContext(reader); | ||
| A.CallTo(() => _readerRow.Context).Returns(context); | ||
|
|
||
| // Act & Assert | ||
| Assert.Throws<TypeConverterException>(() => converter.ConvertFromString("notatime", _readerRow, _memberMapData)); | ||
| // Act | ||
| object action() => converter.ConvertFromString("notatime", _readerRow, _memberMapData); | ||
|
|
||
| // Assert | ||
| var exception = Assert.Throws<TypeConverterException>(action); | ||
| Assert.Contains("Invalid TimeSpan format", exception.Message); | ||
|
Comment on lines
-73
to
+76
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exceptions assertions are awkward, but they can be separated from the The In addition, |
||
| } | ||
|
|
||
| [Theory] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,7 @@ public class SleepExtensionsTests | |
| { | ||
| [Theory] | ||
| [MemberData(nameof(SleepAverageTheoryData))] | ||
| public void GetAverage_ReturnsCorrectAverage(List<Sleep> sleeps, Func<Sleep, decimal> selector, decimal expected) | ||
| public void GetAverage_ReturnsCorrectAverage(Sleep[] sleeps, Func<Sleep, decimal> selector, decimal expected) | ||
| { | ||
| // Act | ||
| var actual = sleeps.GetAverage(selector); | ||
|
|
@@ -42,27 +42,35 @@ public void GetAverage_ReturnsZero_WhenSleepsIsNull() | |
| Assert.Equal(expected, actual); | ||
| } | ||
|
|
||
| public static TheoryData<List<Sleep>, Func<Sleep, decimal>, decimal> SleepAverageTheoryData => new() | ||
| public static TheoryData<Sleep[], Func<Sleep, decimal>, decimal> SleepAverageTheoryData => new() | ||
| { | ||
| { | ||
| new List<Sleep> | ||
| { | ||
| [ | ||
| new() { Bedtime = new TimeSpan(2, 0, 0) }, | ||
| new() { Bedtime = new TimeSpan(3, 0, 0) } | ||
| }, | ||
| ], | ||
|
Comment on lines
-48
to
+51
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because this is static, instead of dynamic data, we can declare this data as an array. It's important to keep in mind that you only need a |
||
| s => s.BedtimeAsDecimal, | ||
| 2.5m | ||
| }, | ||
| { | ||
| new List<Sleep> | ||
| { | ||
| [ | ||
| new() { Waketime = new TimeSpan(6, 0, 0) }, | ||
| new() { Waketime = new TimeSpan(6, 0, 0) }, | ||
| new() { Waketime = new TimeSpan(6, 0, 0) }, | ||
| new() { Waketime = new TimeSpan(6, 0, 0) } | ||
| }, | ||
| ], | ||
| s => s.WaketimeAsDecimal, | ||
| 6m | ||
| }, | ||
| { | ||
| [ | ||
| new() { Waketime = new TimeSpan(2, 15, 0) }, | ||
| new() { Waketime = new TimeSpan(3, 30, 0) }, | ||
| new() { Waketime = new TimeSpan(4, 45, 0) }, | ||
| new() { Waketime = new TimeSpan(5, 50, 0) } | ||
| ], | ||
| s => s.WaketimeAsDecimal, | ||
| 4.08m | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,8 +21,12 @@ public void ToBytes_ThrowsArgumentNullException_OnNullInput() | |
| // Arrange | ||
| string? input = null; | ||
|
|
||
| // Act & Assert | ||
| Assert.Throws<ArgumentNullException>(() => input!.ToBytes()); | ||
| // Act | ||
| byte[] action() => input!.ToBytes(); | ||
|
|
||
| // Assert | ||
| var exception = Assert.Throws<ArgumentNullException>(action); | ||
| Assert.Equal("Input string cannot be null. (Parameter 'str')", exception.Message); | ||
| } | ||
|
|
||
| [Theory] | ||
|
|
@@ -48,13 +52,19 @@ public void ToTimeSpan_ParsesVariousFormats(string timeAsString, int hour, int m | |
| } | ||
|
|
||
| [Theory] | ||
| [InlineData(null)] | ||
| [InlineData("")] | ||
| [InlineData(" ")] | ||
| [InlineData("notatime")] | ||
| public void ToTimeSpan_ThrowsFormatException_OnInvalid(string? time) | ||
| [InlineData(null, "Input string is null or empty.")] | ||
| [InlineData("", "Input string is null or empty.")] | ||
| [InlineData(" ", "Input string is null or empty.")] | ||
| [InlineData("notatime", "String 'notatime' was not recognized as a valid DateTime.")] | ||
|
Comment on lines
+55
to
+58
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a perfect example of why I like to see the Exception message, because occasionally you'll get variation. |
||
| public void ToTimeSpan_ThrowsFormatException_OnInvalid(string? time, string expected) | ||
| { | ||
| // Arrange & Act & Assert | ||
| Assert.Throws<FormatException>(() => time!.ToTimeSpan()); | ||
| // Arrange - See InlineData | ||
|
|
||
| // Act | ||
| object? action() => time!.ToTimeSpan(); | ||
|
|
||
| // Assert | ||
| var exception = Assert.Throws<FormatException>(action); | ||
| Assert.Equal(expected, exception.Message); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,4 @@ | ||
| using RoyAppMaui.Models; | ||
|
|
||
| namespace RoyAppMaui.Tests.Models; | ||
| namespace RoyAppMaui.Models.Tests; | ||
|
Comment on lines
-3
to
+1
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you were mostly good about this in the test classes but missed this in a few of them. It's understandable because, by default Visual Studio puts |
||
| public class SleepTests | ||
| { | ||
| [Fact] | ||
|
|
@@ -9,8 +7,11 @@ public void BedtimeRec_ReturnsDecimalHours() | |
| // Arrange | ||
| var sleep = new Sleep { Bedtime = new TimeSpan(22, 30, 0) }; | ||
|
|
||
| // Act & Assert | ||
| Assert.Equal(22.5m, sleep.BedtimeAsDecimal); | ||
| // Act | ||
| decimal actual = sleep.BedtimeAsDecimal; | ||
|
|
||
| // Assert | ||
| Assert.Equal(22.5m, actual); | ||
|
Comment on lines
-12
to
+14
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know it's extra lines and this is nitpicky, but I always strive to keep my |
||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -19,8 +20,11 @@ public void WaketimeRec_ReturnsDecimalHours() | |
| // Arrange | ||
| var sleep = new Sleep { Waketime = new TimeSpan(6, 15, 0) }; | ||
|
|
||
| // Act & Assert | ||
| Assert.Equal(6.25m, sleep.WaketimeAsDecimal); | ||
| // Act | ||
| decimal actual = sleep.WaketimeAsDecimal; | ||
|
|
||
| // Assert | ||
| Assert.Equal(6.25m, actual); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -29,8 +33,11 @@ public void BedtimeDisplay_ReturnsFormattedString() | |
| // Arrange | ||
| var sleep = new Sleep { Bedtime = new TimeSpan(22, 0, 0) }; | ||
|
|
||
| // Act & Assert | ||
| Assert.Equal("10:00 PM", sleep.BedtimeDisplay); | ||
| // Act | ||
| string actual = sleep.BedtimeDisplay; | ||
|
|
||
| // Assert | ||
| Assert.Equal("10:00 PM", actual); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -39,8 +46,11 @@ public void WaketimeDisplay_ReturnsFormattedString() | |
| // Arrange | ||
| var sleep = new Sleep { Waketime = new TimeSpan(6, 0, 0) }; | ||
|
|
||
| // Act & Assert | ||
| Assert.Equal("06:00 AM", sleep.WaketimeDisplay); | ||
| // Act | ||
| string actual = sleep.WaketimeDisplay; | ||
|
|
||
| // Assert | ||
| Assert.Equal("06:00 AM", actual); | ||
| } | ||
|
|
||
| [Theory] | ||
|
|
@@ -58,7 +68,10 @@ public void Duration_CalculatesCorrectly(int bedHour, int bedMin, int wakeHour, | |
| Waketime = new TimeSpan(wakeHour, wakeMin, 0) | ||
| }; | ||
|
|
||
| // Act & Assert | ||
| Assert.Equal(expected, sleep.Duration); | ||
| // Act | ||
| decimal actual = sleep.Duration; | ||
|
|
||
| // Assert | ||
| Assert.Equal(expected, actual); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,9 @@ | ||
| using RoyAppMaui.Extensions; | ||
| using RoyAppMaui.Models; | ||
| using RoyAppMaui.Services.Impl; | ||
|
|
||
| using System.Text; | ||
|
|
||
| namespace RoyAppMaui.Tests.Services; | ||
| namespace RoyAppMaui.Services.Impl.Tests; | ||
| public class DataServiceTests | ||
| { | ||
| [Fact] | ||
|
|
@@ -19,9 +18,13 @@ public void GetExportData_EmptyList_ReturnsHeaderAndAverages() | |
|
|
||
| // Assert | ||
| var actual = Encoding.UTF8.GetString(result); | ||
| Assert.Contains("Id,Bedtime,Bedtime (as decimal),Waketime,Waketime (as decimal)", actual); | ||
| Assert.Contains("Bedtime Average: 0", actual); | ||
| Assert.Contains("Waketime Average: 0", actual); | ||
| Assert.Equal(""" | ||
| Id,Bedtime,Bedtime (as decimal),Waketime,Waketime (as decimal),Duration | ||
| Bedtime Average: 0 | ||
| Waketime Average: 0 | ||
| Duration Average: 0 | ||
|
|
||
| """, actual); | ||
|
Comment on lines
-22
to
+27
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used to do With Contains, I have found that changes will happen, over time, as you make improvements, and you may not catch, right away, that you've broken your output, because Contains only verifies at a slice. This, above, now verifies the whole thing.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting 🤔 |
||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -42,10 +45,14 @@ public void GetExportData_SingleSleep_ReturnsCorrectCsv() | |
|
|
||
| // Assert | ||
| var actual = Encoding.UTF8.GetString(result); | ||
| Assert.Contains("Id,Bedtime,Bedtime (as decimal),Waketime,Waketime (as decimal)", actual); | ||
| Assert.Contains("1,10:00 PM,22.00,06:00 AM,06.00", actual); | ||
| Assert.Contains("Bedtime Average: 22", actual); | ||
| Assert.Contains("Waketime Average: 6", actual); | ||
| Assert.Equal(""" | ||
| Id,Bedtime,Bedtime (as decimal),Waketime,Waketime (as decimal),Duration | ||
| 1,10:00 PM,22.00,06:00 AM,06.00,08.00 | ||
| Bedtime Average: 22 | ||
| Waketime Average: 6 | ||
| Duration Average: 8 | ||
|
|
||
| """, actual); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -64,9 +71,14 @@ public void GetExportData_MultipleSleeps_ReturnsCorrectAverages() | |
|
|
||
| // Assert | ||
| var actual = Encoding.UTF8.GetString(result); | ||
| Assert.Contains("1,10:00 PM,22.00,06:00 AM,06.00", actual); | ||
| Assert.Contains("2,11:00 PM,23.00,07:00 AM,07.00", actual); | ||
| Assert.Contains("Bedtime Average: 22.5", actual); | ||
| Assert.Contains("Waketime Average: 6.5", actual); | ||
| Assert.Equal(""" | ||
| Id,Bedtime,Bedtime (as decimal),Waketime,Waketime (as decimal),Duration | ||
| 1,10:00 PM,22.00,06:00 AM,06.00,08.00 | ||
| 2,11:00 PM,23.00,07:00 AM,07.00,08.00 | ||
| Bedtime Average: 22.5 | ||
| Waketime Average: 6.5 | ||
| Duration Average: 8 | ||
|
|
||
| """, actual); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,10 @@ | ||
| using CommunityToolkit.Maui.Storage; | ||
|
|
||
| using FakeItEasy; | ||
|
|
||
| using RoyAppMaui.Extensions; | ||
| using RoyAppMaui.Services.Impl; | ||
|
|
||
| using System.IO.Abstractions.TestingHelpers; | ||
|
|
||
| namespace RoyAppMaui.Services.Tests; | ||
| namespace RoyAppMaui.Services.Impl.Tests; | ||
| public class FileServiceTests | ||
| { | ||
| [Theory] | ||
|
|
@@ -36,7 +33,7 @@ public void GetSleepDataFromCsv_ParsesValidCsv_ReturnsSleepRecords(string id, st | |
| // Assert | ||
| Assert.True(result.IsSuccess); | ||
|
|
||
| var actual = result.Value.ToList(); | ||
| var actual = result.Value; | ||
|
Comment on lines
-39
to
+36
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Assert.Single(actual); | ||
| Assert.Equal(id, actual[0].Id); | ||
| Assert.Equal(bedtime.ToTimeSpan(), actual[0].Bedtime); | ||
|
|
@@ -58,7 +55,7 @@ public void GetSleepDataFromCsv_FileNotFound_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("not found", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("The file at nonexistent.csv was not found.", actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -76,7 +73,7 @@ public void GetSleepDataFromCsv_EmptyFilePath_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("null or empty", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("File path is null or empty.", actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -98,7 +95,23 @@ public void GetSleepDataFromCsv_MalformedCsv_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("CSV parsing error", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal(""" | ||
| CSV parsing error: Invalid TimeSpan format: 'data'. | ||
| IReader state: | ||
| ColumnCount: 3 | ||
| CurrentIndex: 1 | ||
| HeaderRecord: | ||
|
|
||
| IParser state: | ||
| ByteCount: 0 | ||
| CharCount: 13 | ||
| Row: 1 | ||
| RawRow: 1 | ||
| Count: 3 | ||
| RawRecord: | ||
| bad,data,here | ||
|
|
||
| """, actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -117,7 +130,7 @@ public void GetSleepDataFromCsv_UnexpectedException_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("unexpected error", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("An unexpected error occurred while reading the file: Value cannot be null. (Parameter 'reader')", actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -161,7 +174,7 @@ public async Task SaveBytesToFileAsync_SaveFails_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("user canceled", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("user canceled", actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -181,7 +194,7 @@ public async Task SaveBytesToFileAsync_UnexpectedException_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("token ring", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("File save error: Oh no, my token ring!", actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -221,7 +234,7 @@ public async Task SelectImportFileAsync_UserCancels_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("user canceled", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("user canceled", actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -240,7 +253,7 @@ public async Task SelectImportFileAsync_FileDoesNotExist_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("does not exist", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("Selected file does not exist.", actual.Errors[0].Message); | ||
| } | ||
|
|
||
| [Fact] | ||
|
|
@@ -261,6 +274,6 @@ public async Task SelectImportFileAsync_FileIsNotCsv_ReturnsFailure() | |
|
|
||
| // Assert | ||
| Assert.True(actual.IsFailed); | ||
| Assert.Contains("not a CSV", actual.Errors[0].Message, StringComparison.OrdinalIgnoreCase); | ||
| Assert.Equal("Selected file is not a CSV file.", actual.Errors[0].Message); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| global using FakeItEasy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to
Usings.csasglobal