Настройка проекта
1dotnet new xunit -n MyApp.Tests2cd MyApp.Tests3dotnet add reference ../MyApp4dotnet add package Moq
Основы xUnit
▸Простой тест
1using Xunit;23public class CalculatorTests4{5 [Fact]6 public void Add_TwoNumbers_ReturnsSum()7 {8 var calc = new Calculator();9 var result = calc.Add(2, 3);10 Assert.Equal(5, result);11 }1213 [Fact]14 public void Divide_ByZero_ThrowsException()15 {16 var calc = new Calculator();17 Assert.Throws<DivideByZeroException>(() => calc.Divide(1, 0));18 }19}
▸Теории (Parameterized Tests)
1public class PrimeNumberTests2{3 [Theory]4 [InlineData(2)]5 [InlineData(3)]6 [InlineData(5)]7 [InlineData(7)]8 [InlineData(11)]9 public void IsPrime_ReturnsTrue_ForPrimeNumbers(int number)10 {11 Assert.True(PrimeChecker.IsPrime(number));12 }1314 [Theory]15 [InlineData(4)]16 [InlineData(6)]17 [InlineData(8)]18 [InlineData(9)]19 public void IsPrime_ReturnsFalse_ForNonPrimeNumbers(int number)20 {21 Assert.False(PrimeChecker.IsPrime(number));22 }23}
Assertions
1// Equal / NotEqual2Assert.Equal(expected, actual);3Assert.NotEqual(1, 2);45// True / False6Assert.True(condition);7Assert.False(condition);89// Null / NotNull10Assert.Null(obj);11Assert.NotNull(obj);1213// Collections14Assert.Contains(item, collection);15Assert.DoesNotContain(item, collection);16Assert.Empty(collection);17Assert.Single(collection);18Assert.Equal(new[] { 1, 2, 3 }, collection);1920// Strings21Assert.StartsWith("Hello", str);22Assert.EndsWith("World", str);23Assert.Contains("lo Wo", str);2425// Throws26Assert.Throws<Exception>(() => method());27await Assert.ThrowsAsync<Exception>(() => methodAsync());
Moq: мокирование
▸Базовое мокирование
1using Moq;23public class UserServiceTests4{5 [Fact]6 public async Task GetUser_ReturnsUser()7 {8 // Arrange9 var mockRepo = new Mock<IUserRepository>();10 mockRepo.Setup(r => r.GetByIdAsync(1))11 .ReturnsAsync(new User { Id = 1, Name = "John" });1213 var service = new UserService(mockRepo.Object);1415 // Act16 var user = await service.GetUserAsync(1);1718 // Assert19 Assert.Equal("John", user.Name);20 mockRepo.Verify(r => r.GetByIdAsync(1), Times.Once);21 }22}
▸Проверка вызовов
1// Проверка, что метод был вызван ровно один раз2mockRepo.Verify(r => r.SaveAsync(It.IsAny<User>()), Times.Once);34// Проверка, что метод НЕ был вызван5mockRepo.Verify(r => r.DeleteAsync(It.IsAny<int>()), Times.Never);67// Проверка с аргументами8mockRepo.Verify(r => r.GetByIdAsync(1), Times.Once);9mockRepo.Verify(r => r.GetByIdAsync(It.Is<int>(id => id > 0)), Times.AtLeastOnce);
▸Callbacks
1User savedUser = null;2mockRepo.Setup(r => r.SaveAsync(It.IsAny<User>()))3 .Callback<User>(u => savedUser = u)4 .ReturnsAsync(new User { Id = 1 });
▸Throws
1mockRepo.Setup(r => r.GetByIdAsync(999))2 .ThrowsAsync(new NotFoundException("User not found"));
Тестирование сервисов
1public class OrderServiceTests2{3 private readonly Mock<IOrderRepository> _orderRepo;4 private readonly Mock<IEmailService> _emailService;5 private readonly OrderService _service;67 public OrderServiceTests()8 {9 _orderRepo = new Mock<IOrderRepository>();10 _emailService = new Mock<IEmailService>();11 _service = new OrderService(_orderRepo.Object, _emailService.Object);12 }1314 [Fact]15 public async Task CreateOrder_SavesAndSendsEmail()16 {17 var dto = new CreateOrderDto { ProductId = 1, Quantity = 2 };1819 await _service.CreateOrderAsync(dto);2021 _orderRepo.Verify(r => r.SaveAsync(It.IsAny<Order>()), Times.Once);22 _emailService.Verify(e => e.SendAsync(23 It.IsAny<string>(),24 It.IsAny<string>(),25 It.IsAny<string>()),26 Times.Once);27 }28}
Заключение
xUnit и Moq — это стандартные инструменты для тестирования C# кода. xUnit предоставляет простой и мощный API для написания тестов, Moq — для мокирования зависимостей. Комбинация этих инструментов позволяет писать надёжные unit-тесты.