using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using Dpz.Core.Entity.Base.Image;
using Dpz.Core.EnumLibrary;
using Dpz.Core.Service.Mediator.Features.Article.Commands;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using Moq;
namespace Dpz.Core.ServiceTest;
[TestFixture]
public class TryDeleteItHomeArticleCommandTest
{
[Test]
public async Task Handle_WhenArticleDoesNotExist_TreatsAsSuccess()
{
var id = ObjectId.GenerateNewId();
var repository = new Mock<IRepository<Article>>();
repository
.Setup(x => x.FindAsync(id, It.IsAny<CancellationToken>()))
.ReturnsAsync((Article?)null);
var handler = CreateHandler(repository);
await handler.Handle(
new TryDeleteItHomeArticleCommand { Id = id.ToString() },
CancellationToken.None
);
repository.Verify(
x =>
x.DeleteAsync(
It.IsAny<Expression<Func<Article, bool>>>(),
It.IsAny<CancellationToken>()
),
Times.Never
);
repository.Verify(
x =>
x.UpdateAsync(
It.IsAny<Expression<Func<Article, bool>>>(),
It.IsAny<UpdateDefinition<Article>>(),
It.IsAny<CancellationToken>()
),
Times.Never
);
}
[Test]
public async Task Handle_WhenArticleIsNotItHome_ClearsImageMetadata()
{
var article = CreateArticle(["Other"]);
var repository = new Mock<IRepository<Article>>();
repository
.Setup(x => x.FindAsync(article.Id, It.IsAny<CancellationToken>()))
.ReturnsAsync(article);
var handler = CreateHandler(repository);
await handler.Handle(
new TryDeleteItHomeArticleCommand { Id = article.Id.ToString() },
CancellationToken.None
);
repository.Verify(
x =>
x.UpdateAsync(
It.IsAny<Expression<Func<Article, bool>>>(),
It.IsAny<UpdateDefinition<Article>>(),
It.IsAny<CancellationToken>()
),
Times.Once
);
repository.Verify(
x =>
x.DeleteAsync(
It.IsAny<Expression<Func<Article, bool>>>(),
It.IsAny<CancellationToken>()
),
Times.Never
);
}
[Test]
public async Task Handle_WhenArticleIsItHome_DeletesArticle()
{
var article = CreateArticle(["ItHome"]);
var repository = new Mock<IRepository<Article>>();
repository
.Setup(x => x.FindAsync(article.Id, It.IsAny<CancellationToken>()))
.ReturnsAsync(article);
var handler = CreateHandler(repository);
await handler.Handle(
new TryDeleteItHomeArticleCommand { Id = article.Id.ToString() },
CancellationToken.None
);
repository.Verify(
x =>
x.DeleteAsync(
It.IsAny<Expression<Func<Article, bool>>>(),
It.IsAny<CancellationToken>()
),
Times.Once
);
repository.Verify(
x =>
x.UpdateAsync(
It.IsAny<Expression<Func<Article, bool>>>(),
It.IsAny<UpdateDefinition<Article>>(),
It.IsAny<CancellationToken>()
),
Times.Never
);
}
private static TryDeleteItHomeArticleCommandHandler CreateHandler(
Mock<IRepository<Article>> repository
)
{
return new TryDeleteItHomeArticleCommandHandler(
repository.Object,
Mock.Of<ILogger<TryDeleteItHomeArticleCommandHandler>>()
);
}
private static Article CreateArticle(List<string> tags)
{
return new Article
{
Id = ObjectId.GenerateNewId(),
Title = "title",
Markdown = "markdown",
Author = new UserInfo
{
Id = "user",
Name = "user",
Avatar = "avatar",
Sex = Sex.Other,
Key = "key",
Permissions = null,
},
Tags = tags,
Images =
[
new ImageMetadata
{
Url = "https://cdn.example.com/a.jpg",
Width = 100,
Height = 100,
},
],
MainImageMetadata = new ImageMetadata
{
Url = "https://cdn.example.com/a.jpg",
Width = 100,
Height = 100,
},
};
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个 NUnit 单元测试类,用于测试 TryDeleteItHomeArticleCommandHandler 的功能。该处理程序负责处理删除"IT之家"文章的命令。
整体结构
测试类使用 Moq 框架来模拟依赖项,测试了处理程序在不同场景下的行为。
三个测试用例
1. Handle_WhenArticleDoesNotExist_TreatsAsSuccess
测试场景:文章不存在时的处理
- 模拟数据库查询返回 null(文章不存在)
- 执行删除命令
- 验证:既没有调用删除操作,也没有调用更新操作
预期行为:将不存在的文章视为成功处理(幂等性设计)
2. Handle_WhenArticleIsNotItHome_ClearsImageMetadata
测试场景:文章存在但标签不是"ItHome"
- 创建一个标签为 "Other" 的文章
- 模拟数据库返回该文章
- 执行删除命令
- 验证:调用了一次更新操作,没有调用删除操作
预期行为:不删除文章,只清理图片元数据(通过更新操作)
3. Handle_WhenArticleIsItHome_DeletesArticle
测试场景:文章标签是"ItHome"
- 创建一个标签为 "ItHome" 的文章
- 模拟数据库返回该文章
- 执行删除命令
- 验证:调用了一次删除操作,没有调用更新操作
预期行为:直接删除文章
辅助方法
CreateHandler
创建被测试的处理程序实例,注入模拟的仓储和日志记录器。
CreateArticle
创建测试用的 Article 对象,包含:
- 随机生成的 ObjectId
- 标题、Markdown 内容
- 用户信息(作者)
- 可自定义的标签列表
- 图片元数据(包含 URL、宽度、高度)
测试目的
这些测试确保 TryDeleteItHomeArticleCommand 处理程序能够:
- 安全处理不存在的文章(避免异常)
- 根据文章来源区别处理:
- IT之家文章:直接删除
- 其他来源文章:仅清理元数据
- 正确调用底层仓储方法(通过 Mock 验证)
这是典型的 行为驱动测试(Behavior-Driven Testing),关注业务逻辑的正确性。
AI 正在分析代码…
评论加载中...