Unit Tests
- Help to find defects and regressions
- Free to run as often as required
- Run at any time, on-demand or scheduled
- Quicker to execute than manual testing
- Find errors sooner
- Generally reliable
- Test code sites with production code
- Happier development teams
Different test types
Unit Tests
Integration Tests
Subcutaneous Tests
User Interface Tests
The logical phases of an automated test
Asserts
|
|
|
|
|
Boolean |
Assert.True(actualValue) |
Assert.True(sut.Value) |
|
String |
Assert.Equal(“expected”, “actual”) |
Assert.Equal(“abc”, “sut.Value”) |
|
Assert.Equal(“expected”, “actual”, ignoreCase: true) |
Assert.Equal(“ABC”, “sut.Value”, ignoreCase: true) |
|
|
Assert.StartWith(“expected”, “actual”) |
Assert.StartWith(“abc”, “sut.Value”) |
|
|
Assert.EndWith(“expected”, “actual”) |
Assert.EndWith(“abc”, “sut.Value”) |
|
|
Assert.Matches(“expectedRegExp”, “actual”) |
Assert.Matches(“[A-Z]”, “sut.Value”) |
|
|
Number |
Assert.Equal(expected, actual) |
Assert.Equal(123, sut.Value) |
|
Assert.NotEqual(expected, actual) |
Assert.NotEqual(123, sut.Value) |
|
|
Assert.True(numberComparison) |
Assert.True(sut.Value >= 123 && sut.Value <= 321) |
|
|
Assert.InRange(accrual, low, high) |
Assert.InRange(sut.Value, 123, 321) |
|
|
Assert.Equal(expected, actual, precision) |
Assert.Equal(123.667, sut.Value, 3) |
|
|
Null |
Assert.Null(actual) |
Assert.Null(sut.Value) |
|
Assert.NotNull(actual) |
Assert.NotNull(sut.Value) |
|
|
Collection |
Assert.Contains(expectedElement, actualCollection) |
Assert.Contains(“a”, sut. Collection) |
|
Assert.DoesNotContain(expectedElement, actualCollection) |
Assert.DoesNotContain(“a”, sut. Collection) |
|
|
Assert.Contains(actualCollection, expectedPredicate) |
Assert.Contains(sut.Collection, collection=> collection.Contains(“abc”) |
|
|
Assert.Contains(expectedCollection, actualCollection) |
Assert.Contains(collection, sut.Collection) |
|
|
Assert against each item in the collection, “All” will loop through
the collection |
Assert.All(sut.Collection, s => Assert.False(s)) |
|
|
Object |
Check the type of object |
Assert.IsType<ClassA>(obj) |
|
Check the type of object is not |
Assert.IsNotType<DateTime>(obj) |
|
|
Check the type for inherited class |
Assert.IsAssignableFrom<ClassA>(obj) |
|
|
Object instances |
Check object references are same |
Assert.Same(obje1, obj2) |
|
Check object references are not same |
Assert.NotSame(obje1, obj2) |
|
|
Throws Exception |
Assert.Throws<expectedException, callingMethod) |
Assert.Throws<ArgumentNullException>(() => sut.Create(null)) |
|
Particular argument causes the error and throws exception |
Assert.Throws< ArgumentNullException>(“name”, () =>
sut.Create(null) |
|
|
Event Raised |
Asserting that events are raised |
Assert.Raises<EventArgs>(handler => sut.Event += handler, handler => sut.Event -= handler, () => sut.Method()); |
|
Asserting Property change event |
Assert.PropertyChanged(sut, “Health”, () => sut.TakeDamage(10)) |
Controlling Test Execution
Categorizing and Running sub tests
public class BossEnemyShould
{
[Fact]
[Trait("Category", "Boss")]
public void HaveCorrectPower()
{
BossEnemy sut = new BossEnemy();
Assert.Equal(166.667, sut.SpecialAttackPower, 3);
}
}
[Trait("Category", "Enemy")]
public class EnemyFactoryShould
{
[Fact]
public void CreateNormalEnemyByDefault()
{
>dotnet testRun tests for specific Trait from dotnet core:
>dotnet test –filter TraitName=TraitValue
>dotnet test --filter Category=Enemy
>dotnet test --filter “Category=Boss|Category=Enemy”
[Fact(Skip = "Don't need to run this")]
public void CreateNormalEnemyByDefault()
{
xUnit Setup and Dispose
public class PlayerCharacterShould : IDisposable
{
private readonly PlayerCharacter _sut;
private readonly ITestOutputHelper _output;
public PlayerCharacterShould(ITestOutputHelper output)
{
_output = output;
_output.WriteLine("Creating new PlayerCharacter");
_sut = new PlayerCharacter();
}
public void Dispose()
{
_output.WriteLine($"Disposing PlayerCharacter {_sut.FullName}");
_sut.Dispose();
}
[Fact]
public void BeInexperiencedWhenNew()
{
Share Context between Tests methods in a test class
public class GameStateFixture : IDisposable
{
public GameState State { get; private set; }
public GameStateFixture()
{
State = new GameState();
}
public void Dispose()
{
// Cleanup
}
}
public class GameStateShould : IClassFixture<GameStateFixture>
{
private readonly GameStateFixture _gameStateFixture;
private readonly ITestOutputHelper _output;
public GameStateShould(GameStateFixture gameStateFixture,
ITestOutputHelper output)
{
_gameStateFixture = gameStateFixture;
_output = output;
}
[Fact]
public void DamageAllPlayersWhenEarthquake()
{
_output.WriteLine($"GameState ID={_gameStateFixture.State.Id}");
var player1 = new PlayerCharacter();
var player2 = new PlayerCharacter();
_gameStateFixture.State.Players.Add(player1);
Share Context across Test Classes
[CollectionDefinition("GameState collection")]
public class GameStateCollection : ICollectionFixture<GameStateFixture> {}
[Collection("GameState collection")]
public class TestClass1
{
private readonly GameStateFixture _gameStateFixture;
private readonly ITestOutputHelper _output;
public TestClass1(GameStateFixture gameStateFixture, ITestOutputHelper output)
{
_gameStateFixture = gameStateFixture;
_output = output;
}
[Fact]
public void Test1()
{
Data-driven Tests
- Inline attribute
- Property / field / method
- Custom attribute
- External data
[Theory]
[InlineData(0, 100)]
[InlineData(1, 99)]
[InlineData(50, 50)]
public void TakeDamage(int damage, int expectedHealth)
{
First save test data in a separate class and write a get method to get all test data
public class InternalHealthDamageTestData
{
//private static readonly List<object[]> Data = new List<object[]>
// {
// new object[] {0, 100},
// new object[] {1, 99},
// new object[] {50, 50},
// new object[] {101, 1}
// };
//public static IEnumerable<object[]> TestData => Data;
public static IEnumerable<object[]> TestData
{
get
{
yield return new object[] { 0, 100 };
yield return new object[] { 1, 99 };
yield return new object[] { 50, 50 };
yield return new object[] { 75, 25 };
yield return new object[] { 101, 1 };
}
}
}
[Theory]
[MemberData(nameof(InternalHealthDamageTestData.TestData),
MemberType = typeof(InternalHealthDamageTestData))]
public void TakeDamage(int damage, int expectedHealth)
{
NonPlayerCharacter sut = new NonPlayerCharacter();
sut.TakeDamage(damage);
Assert.Equal(expectedHealth, sut.Health);
}
public class ExternalHealthDamageTestData
{
public static IEnumerable<object[]> TestData
{
get
{
string[] csvLines = File.ReadAllLines("TestData.csv");
var testCases = new List<object[]>();
foreach (var csvLine in csvLines)
{
IEnumerable<int> values = csvLine.Split(',').Select(int.Parse);
object[] testCase = values.Cast<object>().ToArray();
testCases.Add(testCase);
}
return testCases;
}
}
}
[Theory]
[MemberData(nameof(ExternalHealthDamageTestData.TestData),
MemberType = typeof(ExternalHealthDamageTestData))]
public void TakeDamage(int damage, int expectedHealth)
{
NonPlayerCharacter sut = new NonPlayerCharacter();
sut.TakeDamage(damage);
Assert.Equal(expectedHealth, sut.Health);
}
Create Custom Attribute as the Data Source
[Theory]
[HealthDamageData]
public void TakeDamage(int damage, int expectedHealth)
{
NonPlayerCharacter sut = new NonPlayerCharacter();
sut.TakeDamage(damage);
Assert.Equal(expectedHealth, sut.Health);
}
public class HealthDamageDataAttribute : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[] { 0, 100 };
yield return new object[] { 1, 99 };
yield return new object[] { 50, 50 };
yield return new object[] { 75, 25 };
yield return new object[] { 101, 1 };
}
}
public class HealthDamageDataAttribute : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
string[] csvLines = File.ReadAllLines("TestData.csv");
var testCases = new List<object[]>();
foreach (var csvLine in csvLines)
{
IEnumerable<int> values = csvLine.Split(',').Select(int.Parse);
object[] testCase = values.Cast<object>().ToArray();
testCases.Add(testCase);
}
return testCases;
}
}



No comments:
Post a Comment