dotnet-testing-autofixture-nsubstitute-integration
npx skills add https://github.com/kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-autofixture-nsubstitute-integration
Agent 安装分布
Skill 文档
AutoFixture + NSubstitute èªåæ¨¡æ¬æ´å
æè½æ¦è¿°
æ¬æè½ä»ç´¹å¦ä½æ´å AutoFixture è NSubstituteï¼éé AutoFixture.AutoNSubstitute å¥ä»¶å¯¦ç¾èªå模æ¬ï¼Auto-Mockingï¼åè½ãé種æ´åæ¹å¼å¯ä»¥å¤§å¹
ç°¡åå
·æå¤åç¸ä¾æ§çæåé¡å¥æ¸¬è©¦ï¼è®éç¼è
å°æ³¨æ¼æ¸¬è©¦é輯æ¬èº«ï¼èéç¹ç£çç©ä»¶å»ºç«éç¨ã
é©ç¨æ å¢
ç¶è¢«è¦æ±å·è¡ä»¥ä¸ä»»åæï¼è«ä½¿ç¨æ¤æè½ï¼
- æ¸¬è©¦å ·æå¤åä»é¢ç¸ä¾æ§çæåé¡å¥
- 建ç«èªåæ¨¡æ¬ææä»é¢ç¸ä¾æ§ç測試è¨å®
- 使ç¨
[Frozen]屬æ§ç¢ºä¿ç¸ä¾æ§å¯¦ä¾å¨æ¸¬è©¦ä¸ä¿æä¸è´ - 建ç«å°æ¡ç´çèªè¨ AutoData 屬æ§ä¾æ´åå¤ç¨®å®¢è£½åè¨å®
- çµååºå®æ¸¬è©¦å¼èèªåç¢çç©ä»¶ç忏忏¬è©¦
æ ¸å¿å¹å¼
- æ¸å°æ¨£æ¿ç¨å¼ç¢¼ï¼ä¸éè¦æåçºæ¯åä»é¢å»ºç«
Substitute.For<T>() - èªåèçè¤éç¸ä¾åï¼AutoFixture æèªåè§£æä¸¦å»ºç«æéçç©ä»¶
- æå測試ç¶è·æ§ï¼ç¶å»ºæ§å½å¼è®æ´æï¼æ¸¬è©¦ç¨å¼ç¢¼é常ä¸éè¦åæ¥ä¿®æ¹
- ä¿ææ¸¬è©¦éé»ï¼è®éç¼è å°æ³¨æ¼æ¸¬è©¦é輯èéç©ä»¶å»ºç«
å¥ä»¶å®è£èè¨å®
å¿ è¦å¥ä»¶
# æ ¸å¿å¥ä»¶
dotnet add package AutoFixture.AutoNSubstitute
# ç¸éå¥ä»¶ï¼å¦å°æªå®è£ï¼
dotnet add package AutoFixture
dotnet add package AutoFixture.Xunit2
dotnet add package NSubstitute
dotnet add package xunit
NuGet å¥ä»¶è³è¨
| å¥ä»¶å稱 | ç¨é | NuGet é£çµ |
|---|---|---|
AutoFixture.AutoNSubstitute |
AutoFixture è NSubstitute æ´å | nuget.org |
AutoFixture.Xunit2 |
xUnit æ´åï¼AutoData 屬æ§ï¼ | nuget.org |
NSubstitute |
æ¨¡æ¬æ¡æ¶ | nuget.org |
æ ¸å¿æ¦å¿µ
AutoNSubstituteCustomization çä½ç¨
ç¶å¨ AutoFixture ä¸å å
¥ AutoNSubstituteCustomization æï¼å®æèªåï¼
- 嵿¸¬ä»é¢é¡åï¼ç¶ AutoFixture éå°ä»é¢ææ½è±¡é¡å¥æ
- èªåå»ºç«æ¿èº«ï¼ä½¿ç¨ NSubstitute ç
Substitute.For<T>()å»ºç« Mock ç©ä»¶ - æ³¨å ¥ç¸ä¾æ§ï¼å°éäºæ¿èº«ç©ä»¶æ³¨å ¥å°éè¦ç建æ§å½å¼ä¸
- ä¿æå¯¦ä¾ä¸è´æ§ï¼ç¢ºä¿ç¸åé¡åçæ¿èº«å¨åä¸å測試ä¸ä¿æä¸è´
using AutoFixture;
using AutoFixture.AutoNSubstitute;
// 建ç«å
å« AutoNSubstitute åè½ç Fixture
var fixture = new Fixture().Customize(new AutoNSubstituteCustomization());
// èªåå»ºç«æååå
¶ç¸ä¾æ§
// MyService çææä»é¢ç¸ä¾æ§é½æèªåè®æ NSubstitute çæ¿èº«
var service = fixture.Create<MyService>();
FrozenAttribute åçµæ©å¶
[Frozen] 屬æ§ç¨ä¾æ§å¶æ¸¬è©¦ä¸æåé¡åç實ä¾ï¼
- ç¶åæ¸è¢«æ¨è¨»çº
[Frozen]æï¼AutoFixture æå»ºç«éåé¡å¥çä¸å實ä¾ä¸¦åçµå® - å¾çºå¨æ¸¬è©¦æ¹æ³ä¸é½æä½¿ç¨åä¸åå·²åçµç實ä¾
- éå°æ¼éè¦è¨å®ç¸ä¾æ§è¡çºç¶å¾é©è SUT çæ¸¬è©¦ç¹å¥éè¦
[Theory]
[AutoData]
public async Task TestMethod(
[Frozen] IRepository repository, // éå repository æè¢«åçµ
MyService sut) // sut æä½¿ç¨åä¸å repository
{
// è¨å®åçµå¯¦ä¾çè¡çº
repository.GetAsync(Arg.Any<int>()).Returns(someData);
// SUT å
§é¨ä½¿ç¨çæ¯åä¸å repository 實ä¾
var result = await sut.DoSomething();
}
忏é åºçéè¦æ§
ä½¿ç¨ [Frozen] æï¼åæ¸é åºé常éè¦ï¼
// â
æ£ç¢ºï¼Frozen åæ¸å¨ SUT ä¹å
public async Task TestMethod(
[Frozen] IRepository repository,
MyService sut)
// â é¯èª¤ï¼SUT æä½¿ç¨ä¸åç repository 實ä¾
public async Task TestMethod(
MyService sut,
[Frozen] IRepository repository) // 太æåçµäº
å³çµ±æ¹å¼ vs AutoNSubstitute æ¹å¼
å³çµ±æåæ¹å¼
[Fact]
public async Task TraditionalWay()
{
// Arrange - æåå»ºç«æ¯åç¸ä¾æ§
var repository = Substitute.For<IRepository>();
var logger = Substitute.For<ILogger<OrderService>>();
var notificationService = Substitute.For<INotificationService>();
var cacheService = Substitute.For<ICacheService>();
var sut = new OrderService(repository, logger, notificationService, cacheService);
// è¨å®æ¿èº«è¡çº
repository.GetOrderAsync(Arg.Any<int>()).Returns(someOrder);
// Act
var result = await sut.GetOrderAsync(orderId);
// Assert
result.Should().NotBeNull();
}
åé¡ï¼
- ç¶æåå¢å æ°ç¸ä¾æ§æï¼æææ¸¬è©¦é½éè¦ä¿®æ¹
- 大ééè¤ç
Substitute.For<T>()å¼å« - 測試ç¨å¼ç¢¼åé·ï¼é£ä»¥å¿«éç解測試æå
ä½¿ç¨ AutoNSubstitute æ¹å¼
[Theory]
[AutoDataWithCustomization]
public async Task WithAutoNSubstitute(
[Frozen] IRepository repository,
OrderService sut)
{
// Arrange - ç¸ä¾æ§å·²èªå建ç«ï¼åªéè¨å®éè¦çè¡çº
repository.GetOrderAsync(Arg.Any<int>()).Returns(someOrder);
// Act
var result = await sut.GetOrderAsync(orderId);
// Assert
result.Should().NotBeNull();
}
åªå¢ï¼
- åªé宣åéè¦äºåçç¸ä¾æ§
- å ¶ä»ç¸ä¾æ§ï¼logger, notificationService, cacheServiceï¼èªå建ç«
- 建æ§å½å¼è®æ´æï¼æ¸¬è©¦é常ä¸éè¦ä¿®æ¹
èªè¨ AutoData 屬æ§
çºä»éº¼éè¦èªè¨ AutoData 屬æ§ï¼
å¨å¯¦éå°æ¡ä¸ï¼é常éè¦æ´åå¤ç¨®å®¢è£½åè¨å®ï¼
- AutoNSubstituteCustomizationï¼èªåçºä»é¢å»ºç« NSubstitute æ¿èº«
- å°æ¡ç¹å®ç Customizationï¼å¦ Mapper è¨å®ãé©èå¨è¨å®ç
- ä¸è´ç測試åºç¤è¨æ½ï¼ç¢ºä¿æ´åå°æ¡ä½¿ç¨ç¸åçè¨å®
AutoDataWithCustomizationAttribute 實ä½
using AutoFixture;
using AutoFixture.AutoNSubstitute;
using AutoFixture.Xunit2;
namespace MyProject.Tests.AutoFixtureConfigurations;
/// <summary>
/// å
å«å®¢è£½åè¨å®ç AutoData 屬æ§
/// </summary>
public class AutoDataWithCustomizationAttribute : AutoDataAttribute
{
/// <summary>
/// 建æ§å½å¼
/// </summary>
public AutoDataWithCustomizationAttribute() : base(CreateFixture)
{
}
private static IFixture CreateFixture()
{
var fixture = new Fixture()
.Customize(new AutoNSubstituteCustomization())
.Customize(new MapsterMapperCustomization()) // å°æ¡ç¹å®è¨å®
.Customize(new DomainCustomization()); // é 忍¡åè¨å®
return fixture;
}
}
InlineAutoDataWithCustomizationAttribute 實ä½
ç¨æ¼çµååºå®æ¸¬è©¦å¼èèªåç¢çç©ä»¶ï¼
using AutoFixture;
using AutoFixture.AutoNSubstitute;
using AutoFixture.Xunit2;
namespace MyProject.Tests.AutoFixtureConfigurations;
/// <summary>
/// å
å«å®¢è£½åè¨å®ç InlineAutoData 屬æ§
/// </summary>
public class InlineAutoDataWithCustomizationAttribute : InlineAutoDataAttribute
{
/// <summary>
/// 建æ§å½å¼
/// </summary>
/// <param name="values">åºå®å¼ï¼å°å¡«å
¥æ¸¬è©¦æ¹æ³çåå¹¾å忏ï¼</param>
public InlineAutoDataWithCustomizationAttribute(params object[] values)
: base(new AutoDataWithCustomizationAttribute(), values)
{
}
}
éè¦å¯¦ä½ç´°ç¯
çºä»éº¼ä½¿ç¨ new AutoDataWithCustomizationAttribute() è䏿¯ CreateFixture æ¹æ³ï¼
// â é¯èª¤ï¼InlineAutoDataAttribute éè¦ AutoDataAttributeï¼ä¸æ¯ Func<IFixture>
public InlineAutoDataWithCustomizationAttribute(params object[] values)
: base(CreateFixture, values) // ç·¨è¯é¯èª¤æè¡çºç°å¸¸
// â
æ£ç¢ºï¼å³é AutoDataAttribute 實ä¾
public InlineAutoDataWithCustomizationAttribute(params object[] values)
: base(new AutoDataWithCustomizationAttribute(), values)
åå ï¼
InlineAutoDataAttributeç¹¼æ¿èªCompositeDataAttribute- å®éè¦æ¥æ¶ä¸å
AutoDataAttribute實ä¾ä½çºè³æä¾æºæä¾è - 鿍£å¯ä»¥éç¨
AutoDataWithCustomizationAttributeçææè¨å®
常è¦ç¸ä¾æ§ç客製åèç
IMapper 客製åï¼Mapster ç¯ä¾ï¼
æäºç¸ä¾æ§ä¸é©åä½¿ç¨ Mockï¼èæè©²ä½¿ç¨ç實實ä¾ï¼
using AutoFixture;
using Mapster;
using MapsterMapper;
namespace MyProject.Tests.AutoFixtureConfigurations;
/// <summary>
/// Mapster å°æå¨å®¢è£½å
/// </summary>
public class MapsterMapperCustomization : ICustomization
{
private IMapper? _mapper;
public void Customize(IFixture fixture)
{
fixture.Register(() => this.Mapper);
}
private IMapper Mapper
{
get
{
if (this._mapper is not null)
{
return this._mapper;
}
var typeAdapterConfig = new TypeAdapterConfig();
typeAdapterConfig.Scan(typeof(ServiceMapRegister).Assembly);
this._mapper = new Mapper(typeAdapterConfig);
return this._mapper;
}
}
}
çºä»éº¼ IMapper ä¸ç¨ Mockï¼
- å·¥å ·åç¸ä¾æ§ï¼Mapper 䏿¯æ¥åéè¼¯ï¼æ¯ç©ä»¶å°æå·¥å ·
- é©èå°æéè¼¯ï¼æ¸¬è©¦éè¦é©èå°ææ¯å¦æ£ç¢ºï¼Mock æå¤±å»éåè½å
- è¨å®è¤é度ï¼çºæ¯åå°ææ¹æ³è¨å® Returns åèå¢å è¤é度
- 測試æåï¼æåè¦æ¸¬è©¦æ¥åé輯ï¼ä¸æ¯ Mapper çè¡çº
AutoMapper 客製åç¯ä¾
using AutoFixture;
using AutoMapper;
namespace MyProject.Tests.AutoFixtureConfigurations;
public class AutoMapperCustomization : ICustomization
{
private IMapper? _mapper;
public void Customize(IFixture fixture)
{
fixture.Register(() => this.Mapper);
}
private IMapper Mapper
{
get
{
if (this._mapper is not null)
{
return this._mapper;
}
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddMaps(typeof(MappingProfile).Assembly);
});
this._mapper = configuration.CreateMapper();
return this._mapper;
}
}
}
測試實ä½ç¯ä¾
åºæ¬æ¸¬è©¦ï¼ç¡éè¨å®ç¸ä¾è¡çº
ç¶æ¸¬è©¦åªéè¦é©è SUT æ¬èº«çé輯ï¼å¦åæ¸é©èï¼æï¼
[Theory]
[AutoDataWithCustomization]
public async Task IsExistsAsync_輸å
¥çShipperIdçº0æ_ææåºArgumentOutOfRangeException(
ShipperService sut)
{
// Arrange
var shipperId = 0;
// Act
var exception = await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
() => sut.IsExistsAsync(shipperId));
// Assert
exception.Message.Should().Contain(nameof(shipperId));
}
é²é測試ï¼è¨å®ç¸ä¾è¡çº
ä½¿ç¨ [Frozen] åå¾ç¸ä¾æ§ä¸¦è¨å®å
¶è¡çºï¼
[Theory]
[AutoDataWithCustomization]
public async Task IsExistsAsync_輸å
¥çShipperId_è³æä¸åå¨_æåå³false(
[Frozen] IShipperRepository shipperRepository,
ShipperService sut)
{
// Arrange
var shipperId = 99;
shipperRepository.IsExistsAsync(Arg.Any<int>()).Returns(false);
// Act
var actual = await sut.IsExistsAsync(shipperId);
// Assert
actual.Should().BeFalse();
}
使ç¨èªåç¢ççæ¸¬è©¦è³æ
AutoFixture åæç¢ç SUT 忏¬è©¦è³æï¼
[Theory]
[AutoDataWithCustomization]
public async Task GetAsync_輸å
¥çShipperId_è³ææåå¨_æåå³model(
[Frozen] IShipperRepository shipperRepository,
ShipperService sut,
ShipperModel model) // AutoFixture èªåç¢ç
{
// Arrange
var shipperId = model.ShipperId;
shipperRepository.IsExistsAsync(Arg.Any<int>()).Returns(true);
shipperRepository.GetAsync(Arg.Any<int>()).Returns(model);
// Act
var actual = await sut.GetAsync(shipperId);
// Assert
actual.Should().NotBeNull();
actual.ShipperId.Should().Be(shipperId);
}
忏忏¬è©¦ï¼InlineAutoData
çµååºå®æ¸¬è©¦å¼èèªåç¢çç SUTï¼
[Theory]
[InlineAutoDataWithCustomization(0, 10, nameof(from))]
[InlineAutoDataWithCustomization(-1, 10, nameof(from))]
[InlineAutoDataWithCustomization(1, 0, nameof(size))]
[InlineAutoDataWithCustomization(1, -1, nameof(size))]
public async Task GetCollectionAsync_fromèsize輸å
¥ä¸åè¦æ ¼å
§å®¹_ææåºArgumentOutOfRangeException(
int from,
int size,
string parameterName,
ShipperService sut) // èªåç¢ç
{
// Act
var exception = await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
() => sut.GetCollectionAsync(from, size));
// Assert
exception.Message.Should().Contain(parameterName);
}
ä½¿ç¨ CollectionSize æ§å¶éå大å°
[Theory]
[AutoDataWithCustomization]
public async Task GetAllAsync_è³æè¡¨è£¡æ10çè³æ_åå³çéå裡æ10ç(
[Frozen] IShipperRepository shipperRepository,
ShipperService sut,
[CollectionSize(10)] IEnumerable<ShipperModel> models)
{
// Arrange
shipperRepository.GetAllAsync().Returns(models);
// Act
var actual = await sut.GetAllAsync();
// Assert
actual.Should().NotBeEmpty();
actual.Should().HaveCount(10);
}
è¤éè³æè¨å®ï¼ä½¿ç¨ IFixture
ç¶éè¦ç²¾ç¢ºæ§å¶æ¸¬è©¦è³ææï¼
[Theory]
[AutoDataWithCustomization]
public async Task SearchAsync_companyName輸å
¥è³æ_æç¬¦åæ¢ä»¶çè³æ_åå³éåæå
å«ç¬¦åæ¢ä»¶çè³æ(
IFixture fixture,
[Frozen] IShipperRepository shipperRepository,
ShipperService sut)
{
// Arrange
const string companyName = "test";
var models = fixture.Build<ShipperModel>()
.With(x => x.CompanyName, companyName)
.CreateMany(1);
shipperRepository.GetTotalCountAsync().Returns(1);
shipperRepository.SearchAsync(Arg.Any<string>(), Arg.Any<string>())
.Returns(models);
// Act
var actual = await sut.SearchAsync(companyName, string.Empty);
// Assert
actual.Should().NotBeEmpty();
actual.Should().HaveCount(1);
actual.Any(x => x.CompanyName == companyName).Should().BeTrue();
}
Nullable åèé¡åèç
測試 null æç©ºå¼åæ¸æçèçæ¹å¼ï¼
[Theory]
[InlineAutoDataWithCustomization(null!, null!)]
[InlineAutoDataWithCustomization("", "")]
[InlineAutoDataWithCustomization(" ", " ")]
public async Task SearchAsync_companyNameèphoneé½çºç©ºç½_ææåºArgumentException(
string? companyName,
string? phone,
ShipperService sut)
{
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => sut.SearchAsync(companyName!, phone!));
exception.Message.Should().Contain("companyName è phone ä¸å¯é½çºç©ºç½");
}
èç說æï¼
- 忏宣å使ç¨
string?ï¼å çºæ¸¬è©¦éè¦å³å ¥nullå¼ - InlineAutoData ä¸ä½¿ç¨
null!ï¼å訴編è¯å¨éæ¯å»æçæ¸¬è©¦è³æ - æ¹æ³å¼å«ä½¿ç¨
!éç®åï¼å¨æ¸¬è©¦ä¸ä½¿ç¨ null-forgiving éç®å
é©ç¨å ´æ¯å¤æ·
建è°ä½¿ç¨çå ´æ¯
| å ´æ¯ | åå |
|---|---|
| æå層測試 | é常æå¤åç¸ä¾æ§ï¼èªåæ¨¡æ¬æçæå¤§ |
| è¤éç¸ä¾å | AutoFixture èªåèçå¤å±¤ç¸ä¾æ§ |
| 忏忏¬è©¦ | çµååºå®å¼èèªåç¢çè³æ |
| éè¦å¤§éæ¸¬è©¦è³æ | æ¸å°æåå»ºç«æ¸¬è©¦è³æçå·¥ä½ |
| å¿«éè¿ä»£éç¼ | 建æ§å½å¼è®æ´ææ¸¬è©¦é常ä¸éä¿®æ¹ |
謹æ 使ç¨çå ´æ¯
| å ´æ¯ | åå |
|---|---|
| å®ä¸ç¸ä¾æ§æ¸¬è©¦ | æå建ç«å¯è½æ´æ¸ æ°ç´è¦º |
| 精確æ§å¶å±¬æ§å¼ | éè¦é¡å¤ç fixture.Build().With() è¨å® |
| åéä¸çæ AutoFixture | å¸ç¿ææ¬å¯è½å½±é¿éç¼æç |
| é¤é¯å°é£çå ´æ¯ | èªåç¢ççç©ä»¶å¯è½è®é¤é¯è®è¤é |
| æè½ææç測試 | ç©ä»¶å»ºç«çéé·å¯è½å½±é¿å·è¡é度 |
æä½³å¯¦è¸
å°å ¥çç¥
-
漸é²å¼æ¡ç¨
- å¾ç°¡å®çæåé¡å¥éå§
- 鿥æ´å±å°è¤éå ´æ¯
- è®åééæ¼¸çææ¨¡å¼
-
åéå¹è¨
- 確ä¿åéçè§£ Frozen æ©å¶
- 說æåæ¸é åºçéè¦æ§
- å享é¤é¯æå·§
-
建ç«è¦ç¯
- ä½æä½¿ç¨èªåç¢ç vs æå建ç«
- èªè¨ Customization çå½åèçµç¹
- æ¸¬è©¦è³æçæ§å¶çç¥
ç¨å¼ç¢¼çµç¹
MyProject.Tests/
âââ AutoFixtureConfigurations/
â âââ AutoDataWithCustomizationAttribute.cs
â âââ InlineAutoDataWithCustomizationAttribute.cs
â âââ AutoMapperCustomization.cs
â âââ DomainCustomization.cs
âââ Services/
â âââ OrderServiceTests.cs
â âââ ShipperServiceTests.cs
âââ ...
å½åæ £ä¾
- èªè¨ AutoData 屬æ§ï¼
[å°æ¡å稱]AutoDataAttributeæAutoDataWithCustomizationAttribute - Customization é¡å¥ï¼
[åè½]Customizationï¼å¦MapsterMapperCustomizationï¼ - æ¸¬è©¦æ¹æ³ï¼ç¶æ
æ¹æ³_æ å¢_é æçå½å模å¼
注æäºé èéå¶
常è¦é·é±
-
忏é åºé¯èª¤
// â Frozen åæ¸å¨ SUT ä¹å¾ï¼ä¸æçæ public void Test(MyService sut, [Frozen] IRepository repo) // â Frozen åæ¸å¿ é å¨ SUT ä¹å public void Test([Frozen] IRepository repo, MyService sut) -
éºå¿ AutoNSubstituteCustomization
// â æ²æ AutoNSubstituteï¼ä»é¢æç¢çç°å¸¸ var fixture = new Fixture(); // â å å ¥ AutoNSubstituteCustomization var fixture = new Fixture().Customize(new AutoNSubstituteCustomization()); -
é度ä¾è³´èªåç¢ç
// â æ¸¬è©¦æåä¸æç¢º public void Test(Order order, Customer customer, MyService sut) { var result = sut.Process(order); result.Should().NotBeNull(); // é©èä»éº¼ï¼ } // â æç¢ºæ§å¶ééµå±¬æ§ public void Test(IFixture fixture, MyService sut) { var order = fixture.Build<Order>() .With(o => o.Status, OrderStatus.Pending) .Create(); var result = sut.Process(order); result.Status.Should().Be(OrderStatus.Processed); }
æè½èé
- æ¯åæ¸¬è©¦æ¹æ³é½æå»ºç«æ°ç Fixture åææç¸ä¾æ§
- è¤éç©ä»¶åå¯è½å¢å 測試å·è¡æé
- èæ
®ä½¿ç¨
[ClassData]æIClassFixture<T>å ±äº«è¨å®
ç¸éæè½
| æè½å稱 | éè¯èªªæ |
|---|---|
autofixture-basics |
AutoFixture åºç¤ä½¿ç¨ï¼æ¬æè½çåç½®ç¥è |
autofixture-customization |
èªè¨ Customization çé²éç¨æ³ |
autodata-xunit-integration |
AutoData 屬æ§å®¶æç宿´èªªæ |
nsubstitute-mocking |
NSubstitute åºç¤ï¼Mock è¨å®ç詳細說æ |
åèè³æº
åå§æç«
æ¬æè½å §å®¹æç èªãèæ´¾è»é«å·¥ç¨å¸«ç測試修練 – 30 å¤©ææ°ãç³»åæç« ï¼
- Day 13 – AutoFixture æ´å NSubstituteï¼èªåå»ºç« Mock å°è±¡
- éµäººè³½æç« ï¼https://ithelp.ithome.com.tw/articles/10375419
- ç¯ä¾ç¨å¼ç¢¼ï¼https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day13
宿¹æä»¶
- AutoFixture.AutoNSubstitute NuGet Package
- AutoFixture Documentation – Auto Mocking
- NSubstitute Documentation
延伸é±è®
ç¯ä¾æªæ¡
è«åèåç®éä¸çç¯ä¾ç¨å¼ç¢¼ï¼
- custom-autodata-attributes.cs – èªè¨ AutoData 屬æ§ç¯æ¬
- frozen-patterns.cs – Frozen æ©å¶ä½¿ç¨æ¨¡å¼
- service-testing-examples.cs – æåå±¤æ¸¬è©¦å®æ´ç¯ä¾