NUnit
NUnit
概要
NUnitは、すべての.NET言語に対応するオープンソースの単体テストフレームワークです。2024年現在、最新バージョンはNUnit 4.3.2で、.NET Frameworkから.NET Coreまで幅広い環境をサポートしています。Test-Driven Development(TDD)から本格的なシステム・統合テストまで、あらゆるテストシナリオに対応できる包括的なテストソリューションを提供します。
詳細
主要な特徴
現代的なフレームワーク設計
- NUnit 4は最新の.NET機能とC#言語構造を活用
- 最小サポートターゲットは.NET Framework 4.6.2および.NET 6.0
- クロスプラットフォーム対応(Windows、macOS、Linux)
豊富なアサーション機能
- 流暢なアサーションモデル(Fluent Assertion)
Assert.That()メソッドによる可読性の高いテスト記述- 制約ベースのアサーション(Is., Does., Has., Contains.)
柔軟なテスト属性
[TestFixture]、[Test]、[TestCase]などの包括的な属性セット- パラメータ化テストの強力なサポート
[SetUp]、[TearDown]による柔軟なフィクスチャ管理
パフォーマンス向上
- 大規模コードベースでのテスト実行速度大幅改善
CollectionAssert.AreEquivalentの性能問題修正- コレクションの深い比較の最適化
メリット・デメリット
メリット
- 成熟したエコシステム: 長年の開発により安定性と信頼性が確立
- 豊富なドキュメント: 包括的な公式ドキュメントとコミュニティサポート
- IDE統合: Visual Studio、Rider、VS Codeでの優れた統合
- 柔軟性: 単純な単体テストから複雑な統合テストまで対応
- 広範なサポート: すべての.NET言語とフレームワークバージョンに対応
デメリット
- 学習曲線: 初心者には豊富な機能が複雑に感じる場合がある
- セットアップの複雑さ: 大規模プロジェクトでの初期設定が煩雑
- パフォーマンス: 非常に大きなテストスイートでは実行時間が課題となる場合
- 移行コスト: NUnit 3からNUnit 4への移行には注意深い準備が必要
参考ページ
- 公式サイト: NUnit.org
- 公式ドキュメント: NUnit Documentation
- GitHub: nunit/nunit
- NuGet: NUnit Package
- Microsoft Learn: .NET Core単体テスト
書き方の例
基本的なテスト構造
using NUnit.Framework;
[TestFixture]
public class CalculatorTests
{
private Calculator _calculator;
[SetUp]
public void Setup()
{
_calculator = new Calculator();
}
[Test]
public void Add_TwoPositiveNumbers_ReturnsCorrectSum()
{
// Arrange
int a = 5;
int b = 3;
int expected = 8;
// Act
int result = _calculator.Add(a, b);
// Assert
Assert.That(result, Is.EqualTo(expected));
}
}
パラメータ化テスト
[TestFixture]
public class MathOperationsTests
{
[TestCase(2, 3, 5)]
[TestCase(10, 15, 25)]
[TestCase(-1, 1, 0)]
[TestCase(0, 0, 0)]
public void Add_VariousInputs_ReturnsExpectedResult(int a, int b, int expected)
{
var calculator = new Calculator();
int result = calculator.Add(a, b);
Assert.That(result, Is.EqualTo(expected));
}
[TestCase(10, 2, ExpectedResult = 5)]
[TestCase(15, 3, ExpectedResult = 5)]
[TestCase(100, 10, ExpectedResult = 10)]
public int Divide_ValidInputs_ReturnsQuotient(int dividend, int divisor)
{
var calculator = new Calculator();
return calculator.Divide(dividend, divisor);
}
}
アサーションの高度な使用
[TestFixture]
public class AdvancedAssertionTests
{
[Test]
public void StringAssertions_Examples()
{
string actual = "Hello, World!";
Assert.That(actual, Does.StartWith("Hello"));
Assert.That(actual, Does.EndWith("World!"));
Assert.That(actual, Does.Contain("World"));
Assert.That(actual, Is.Not.Null.And.Not.Empty);
}
[Test]
public void CollectionAssertions_Examples()
{
var numbers = new List<int> { 1, 2, 3, 4, 5 };
Assert.That(numbers, Has.Count.EqualTo(5));
Assert.That(numbers, Contains.Item(3));
Assert.That(numbers, Is.All.GreaterThan(0));
Assert.That(numbers, Is.Ordered.Ascending);
}
[Test]
public void ExceptionAssertions_Examples()
{
var calculator = new Calculator();
Assert.Throws<DivideByZeroException>(() => calculator.Divide(10, 0));
var ex = Assert.Throws<ArgumentException>(() =>
calculator.CalculateSquareRoot(-1));
Assert.That(ex.Message, Does.Contain("negative"));
}
}
フィクスチャのライフサイクル管理
[TestFixture]
public class DatabaseTests
{
private DatabaseConnection _connection;
private TestDataBuilder _dataBuilder;
[OneTimeSetUp]
public void OneTimeSetup()
{
// テストフィクスチャ全体で一度だけ実行
_connection = new DatabaseConnection("test_connection_string");
_connection.Connect();
}
[SetUp]
public void Setup()
{
// 各テストメソッドの前に実行
_dataBuilder = new TestDataBuilder(_connection);
_dataBuilder.CreateTestData();
}
[Test]
public void GetUser_ValidId_ReturnsUser()
{
// Arrange
int userId = _dataBuilder.CreateTestUser("John", "[email protected]");
var userService = new UserService(_connection);
// Act
var user = userService.GetUser(userId);
// Assert
Assert.That(user, Is.Not.Null);
Assert.That(user.Name, Is.EqualTo("John"));
Assert.That(user.Email, Is.EqualTo("[email protected]"));
}
[TearDown]
public void TearDown()
{
// 各テストメソッドの後に実行
_dataBuilder.CleanupTestData();
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
// テストフィクスチャ全体で一度だけ実行
_connection.Disconnect();
}
}
非同期テスト
[TestFixture]
public class AsyncTests
{
[Test]
public async Task GetDataAsync_ValidRequest_ReturnsData()
{
// Arrange
var service = new DataService();
var request = new DataRequest { Id = 123 };
// Act
var result = await service.GetDataAsync(request);
// Assert
Assert.That(result, Is.Not.Null);
Assert.That(result.Id, Is.EqualTo(123));
}
[Test]
public void AsyncOperation_Timeout_ThrowsException()
{
var service = new SlowService();
Assert.ThrowsAsync<TimeoutException>(async () =>
await service.SlowOperationAsync(TimeSpan.FromMilliseconds(100)));
}
}
カテゴリとタグによるテスト分類
[TestFixture]
[Category("Integration")]
public class IntegrationTests
{
[Test]
[Category("Database")]
[Category("Slow")]
public void DatabaseIntegration_Test()
{
// データベース統合テスト
}
[Test]
[Category("API")]
public void ApiIntegration_Test()
{
// API統合テスト
}
}
[TestFixture]
[Category("Unit")]
public class UnitTests
{
[Test]
[Category("Fast")]
public void Unit_Test()
{
// 高速な単体テスト
}
}