anduin ha revisionato questo gist 1 week ago. Vai alla revisione
1 file changed, 11 insertions, 15 deletions
fix_entity.md
| @@ -2,6 +2,8 @@ Aiursoft 有一套自己的 Entity Framework Core 实体建模规范。 | |||
| 2 | 2 | ||
| 3 | 3 | 这套规范非常严格,内容如下: | |
| 4 | 4 | ||
| 5 | + | # Aiursoft Entity Framework Core 实体建模规范 | |
| 6 | + | ||
| 5 | 7 | ## 核心哲学:独裁模式 (Dictator Mode) | |
| 6 | 8 | ||
| 7 | 9 | 1. **DbSet 独裁**:数据的增删改(CUD)必须通过 `DbContext.DbSet<T>` 进行。 | |
| @@ -48,12 +50,8 @@ Aiursoft 有一套自己的 Entity Framework Core 实体建模规范。 | |||
| 48 | 50 | ||
| 49 | 51 | * *理由*:在 `new Entity()` 时我们通常只赋值 ID 而不赋值对象,声明为可空是为了满足 C\# 编译器的初始化检查,避免虚假的警告。 | |
| 50 | 52 | ||
| 51 | - | **2.4 禁止 Attribute 冲突** | |
| 52 | - | 禁止在可空类型(`?`)上使用 `[NotNull]`。 | |
| 53 | - | ||
| 54 | - | * 如果业务逻辑上该字段允许为空,使用 `string?`。 | |
| 55 | - | * 如果业务逻辑上该字段不允许为空,使用 `string` 或 `required string`。 | |
| 56 | - | * *导航属性特例*:如 2.3 所述,导航属性必须是 `Type?`,且不得标记 `[NotNull]`。 | |
| 53 | + | **2.4 导航属性必须 NotNull** | |
| 54 | + | 所有**导航引用属性**必须被 `[NotNull]` 修饰。 | |
| 57 | 55 | ||
| 58 | 56 | **2.5 序列化屏蔽** | |
| 59 | 57 | 所有**导航引用属性**必须被 `[Newtonsoft.Json.JsonIgnore]` 修饰(防止死循环)。 | |
| @@ -69,14 +67,12 @@ Aiursoft 有一套自己的 Entity Framework Core 实体建模规范。 | |||
| 69 | 67 | > ```csharp | |
| 70 | 68 | > // 外键 ID (必填) | |
| 71 | 69 | > public required Guid UserId { get; set; } | |
| 72 | - | > ``` | |
| 73 | - | ||
| 74 | - | > // 导航引用 (必须可空,禁止 virtual,禁止 NotNull) | |
| 70 | + | > | |
| 71 | + | > // 导航引用 (必须可空,必须 NotNull,禁止 virtual) | |
| 75 | 72 | > [JsonIgnore] | |
| 76 | 73 | > [ForeignKey(nameof(UserId))] | |
| 77 | - | > public User? User { get; init; } | |
| 78 | - | > | |
| 79 | - | > ``` | |
| 74 | + | > [NotNull] | |
| 75 | + | > public User? User { get; set; } | |
| 80 | 76 | > ``` | |
| 81 | 77 | ||
| 82 | 78 | ----- | |
| @@ -161,12 +157,12 @@ public class TemplateEntity | |||
| 161 | 157 | public required Guid ParentId { get; set; } | |
| 162 | 158 | ||
| 163 | 159 | // [规则 2.3, 2.4, 2.5, 2.6] | |
| 164 | - | // 导航引用:Type?, JsonIgnore, ForeignKey | |
| 160 | + | // 导航引用:Type?, JsonIgnore, ForeignKey, NotNull | |
| 165 | 161 | // 严禁 virtual (禁用延迟加载) | |
| 166 | - | // 严禁 [NotNull] (避免编译器歧义) | |
| 167 | 162 | [JsonIgnore] | |
| 168 | 163 | [ForeignKey(nameof(ParentId))] | |
| 169 | - | public ParentEntity? Parent { get; init; } | |
| 164 | + | [NotNull] | |
| 165 | + | public ParentEntity? Parent { get; set; } | |
| 170 | 166 | ||
| 171 | 167 | // [规则 3.1, 3.2, 3.3] | |
| 172 | 168 | // 集合:IEnumerable (独裁模式), InverseProperty, new List() | |
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
1 file changed, 17 insertions
strict.md(file creato)
| @@ -0,0 +1,17 @@ | |||
| 1 | + | # 严格化 | |
| 2 | + | ||
| 3 | + | 我受够了你的代码了!你为了快速实现功能,选择了非常糙快猛的方法。 | |
| 4 | + | ||
| 5 | + | 你的各种Helper,像极了初级程序员不会OO,把一样的代码腾了个函数。很多对象本身的行为你不会抽象! | |
| 6 | + | ||
| 7 | + | 很多复杂的验证逻辑你不会封装,封装出来的函数名字极其困惑我都不知道俩有什么区别。 | |
| 8 | + | ||
| 9 | + | 而且你不喜欢抛错。我猜你可能是为了拟真真实的生产环境容忍噪音,但是你suppress错误我是完全不理解。现在在开发阶段,什么都应该严格着来。 | |
| 10 | + | ||
| 11 | + | 你要赶紧反思,不要用现在这种遇到错误就suppress、遇到feature就糊一段的方式写代码; | |
| 12 | + | ||
| 13 | + | 而是仔细思考尽量让每一层都透明、都函数式、都面向对象,都封装好。 | |
| 14 | + | ||
| 15 | + | 别人直接调用你的类,就能完成它的设计功能,而内部处理好安全、验证。 | |
| 16 | + | ||
| 17 | + | 你做不到吗?反思,检讨、仔细设计,然后重构!代码要像基类一样暴露出来的api非常简明清晰! | |
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
1 file changed, 1 insertion
fix_entity.md
| @@ -7,6 +7,7 @@ Aiursoft 有一套自己的 Entity Framework Core 实体建模规范。 | |||
| 7 | 7 | 1. **DbSet 独裁**:数据的增删改(CUD)必须通过 `DbContext.DbSet<T>` 进行。 | |
| 8 | 8 | 2. **显式加载**:禁止任何形式的隐式行为(如延迟加载),数据必须显式 `Include`。 | |
| 9 | 9 | 3. **编译期契约**:利用 C\# 类型系统(Type System)在编译期暴露潜在错误,而不是推迟到运行期。 | |
| 10 | + | 4. **表格可见**: 所有实体类必须清晰地表达其数据列和关系,避免隐式约定。所有实体类必须出现在 `DbContext` 中的 `public DbSet<Repo> Repos => Set<Repo>();`,表名必须是实体类名的复数形式(如 `User` 对应 `Users` 表)。 | |
| 10 | 11 | ||
| 11 | 12 | ----- | |
| 12 | 13 | ||
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
1 file changed, 218 insertions
fix_entity.md(file creato)
| @@ -0,0 +1,218 @@ | |||
| 1 | + | Aiursoft 有一套自己的 Entity Framework Core 实体建模规范。 | |
| 2 | + | ||
| 3 | + | 这套规范非常严格,内容如下: | |
| 4 | + | ||
| 5 | + | ## 核心哲学:独裁模式 (Dictator Mode) | |
| 6 | + | ||
| 7 | + | 1. **DbSet 独裁**:数据的增删改(CUD)必须通过 `DbContext.DbSet<T>` 进行。 | |
| 8 | + | 2. **显式加载**:禁止任何形式的隐式行为(如延迟加载),数据必须显式 `Include`。 | |
| 9 | + | 3. **编译期契约**:利用 C\# 类型系统(Type System)在编译期暴露潜在错误,而不是推迟到运行期。 | |
| 10 | + | ||
| 11 | + | ----- | |
| 12 | + | ||
| 13 | + | ## 1\. 主键规范 (Primary Keys) | |
| 14 | + | ||
| 15 | + | **1.1 类型限制** | |
| 16 | + | 所有实体类的主键必须是 `int` 或 `Guid` 类型。 | |
| 17 | + | ||
| 18 | + | * **例外**:直接继承自 `Microsoft.AspNetCore.Identity.IdentityUser` 的实体除外(默认使用 `string`,允许保持原样)。 | |
| 19 | + | ||
| 20 | + | **1.2 强制特性** | |
| 21 | + | 所有实体类的主键必须被 `[Key]` Attribute 修饰。 | |
| 22 | + | ||
| 23 | + | **1.3 不可变性** | |
| 24 | + | 所有实体类的主键必须是 `{ get; init; }`(创建后不可变)。 | |
| 25 | + | ||
| 26 | + | > **示例:** | |
| 27 | + | > | |
| 28 | + | > ```csharp | |
| 29 | + | > [Key] | |
| 30 | + | > public Guid Id { get; init; } | |
| 31 | + | > ``` | |
| 32 | + | ||
| 33 | + | ----- | |
| 34 | + | ||
| 35 | + | ## 2\. 外键与导航规范 (Foreign Keys & Navigation) | |
| 36 | + | ||
| 37 | + | **2.1 成对定义** | |
| 38 | + | 所有外键关系必须显式定义两个属性:**外键ID** 和 **导航引用**。 | |
| 39 | + | ||
| 40 | + | **2.2 外键ID 必填性** | |
| 41 | + | ||
| 42 | + | * **必填关系**:外键ID 必须是 `Guid` (或 `int`),且属性必须是 `required`。 | |
| 43 | + | * **可选关系**:外键ID 必须是 `Guid?` (或 `int?`),且属性必须是 `required` (强制显式赋值 `null`)。 | |
| 44 | + | ||
| 45 | + | **2.3 导航引用可空性** | |
| 46 | + | 所有**导航引用属性**必须声明为**可空类型**(如 `User?`)。 | |
| 47 | + | ||
| 48 | + | * *理由*:在 `new Entity()` 时我们通常只赋值 ID 而不赋值对象,声明为可空是为了满足 C\# 编译器的初始化检查,避免虚假的警告。 | |
| 49 | + | ||
| 50 | + | **2.4 禁止 Attribute 冲突** | |
| 51 | + | 禁止在可空类型(`?`)上使用 `[NotNull]`。 | |
| 52 | + | ||
| 53 | + | * 如果业务逻辑上该字段允许为空,使用 `string?`。 | |
| 54 | + | * 如果业务逻辑上该字段不允许为空,使用 `string` 或 `required string`。 | |
| 55 | + | * *导航属性特例*:如 2.3 所述,导航属性必须是 `Type?`,且不得标记 `[NotNull]`。 | |
| 56 | + | ||
| 57 | + | **2.5 序列化屏蔽** | |
| 58 | + | 所有**导航引用属性**必须被 `[Newtonsoft.Json.JsonIgnore]` 修饰(防止死循环)。 | |
| 59 | + | ||
| 60 | + | **2.6 禁止 Virtual (禁止延迟加载)** | |
| 61 | + | 所有导航属性**严禁**使用 `virtual` 关键字。 | |
| 62 | + | ||
| 63 | + | * *后果*:彻底禁用 Lazy Loading Proxy。 | |
| 64 | + | * *操作要求*:查询关联数据时,必须显式调用 `.Include()` 和 `.ThenInclude()`。 | |
| 65 | + | ||
| 66 | + | > **示例:** | |
| 67 | + | > | |
| 68 | + | > ```csharp | |
| 69 | + | > // 外键 ID (必填) | |
| 70 | + | > public required Guid UserId { get; set; } | |
| 71 | + | > ``` | |
| 72 | + | ||
| 73 | + | > // 导航引用 (必须可空,禁止 virtual,禁止 NotNull) | |
| 74 | + | > [JsonIgnore] | |
| 75 | + | > [ForeignKey(nameof(UserId))] | |
| 76 | + | > public User? User { get; init; } | |
| 77 | + | > | |
| 78 | + | > ``` | |
| 79 | + | > ``` | |
| 80 | + | ||
| 81 | + | ----- | |
| 82 | + | ||
| 83 | + | ## 3\. 集合与独裁规范 (Collections & Dictatorship) | |
| 84 | + | ||
| 85 | + | **3.1 类型限制** | |
| 86 | + | 所有反向导航集合必须声明为 `IEnumerable<T>` 类型。 | |
| 87 | + | ||
| 88 | + | * *目的*:在编译层面阉割集合的 `.Add()` 和 `.Remove()` 方法。 | |
| 89 | + | ||
| 90 | + | **3.2 初始化** | |
| 91 | + | 所有集合属性必须初始化为 `new List<T>()` 以防止空引用异常。 | |
| 92 | + | ||
| 93 | + | **3.3 显式关联** | |
| 94 | + | 所有集合属性必须被 `[InverseProperty]` 修饰。 | |
| 95 | + | ||
| 96 | + | **3.4 独裁修改原则** | |
| 97 | + | 禁止将 `IEnumerable` 强转为 `List` 来操作数据。 | |
| 98 | + | ||
| 99 | + | * **增加子项**:必须创建新对象并 `_dbContext.Add(newItem)`。 | |
| 100 | + | * **删除子项**:必须查询出对象并 `_dbContext.Remove(item)`。 | |
| 101 | + | ||
| 102 | + | > **示例:** | |
| 103 | + | > | |
| 104 | + | > ```csharp | |
| 105 | + | > [InverseProperty(nameof(ExamPaperSubmission.User))] | |
| 106 | + | > public IEnumerable<ExamPaperSubmission> Submissions { get; init; } = new List<ExamPaperSubmission>(); | |
| 107 | + | > ``` | |
| 108 | + | ||
| 109 | + | ----- | |
| 110 | + | ||
| 111 | + | ## 4\. 数据列规范 (Data Columns) | |
| 112 | + | ||
| 113 | + | **4.1 长度约束** | |
| 114 | + | 所有 `string` 或 `byte[]` 类型的列必须被 `[MaxLength]` 约束。 | |
| 115 | + | ||
| 116 | + | **4.2 空值语义** | |
| 117 | + | ||
| 118 | + | * **不可空列**:使用非空类型(如 `string`),严禁使用 `[NotNull]` 修饰可空类型。 | |
| 119 | + | * **可空列**:使用可空类型(如 `string?`),且必须在 XML 注释中说明**为空代表什么业务状态**。 | |
| 120 | + | ||
| 121 | + | **4.3 不可变属性** | |
| 122 | + | 所有业务上不可编辑的属性(如 `CreationTime`),必须是 `{ get; init; }`。 | |
| 123 | + | ||
| 124 | + | ----- | |
| 125 | + | ||
| 126 | + | ## 5\. 完整标准模板 (V3.0) | |
| 127 | + | ||
| 128 | + | ```csharp | |
| 129 | + | using System.ComponentModel.DataAnnotations; | |
| 130 | + | using System.ComponentModel.DataAnnotations.Schema; | |
| 131 | + | using System.Diagnostics.CodeAnalysis; // 尽量少用,除非用于辅助方法 | |
| 132 | + | using Newtonsoft.Json; | |
| 133 | + | ||
| 134 | + | namespace Aiursoft.Exam.Entities; | |
| 135 | + | ||
| 136 | + | public class TemplateEntity | |
| 137 | + | { | |
| 138 | + | // [规则 1.1, 1.2, 1.3] 主键:Guid/int, Key, init | |
| 139 | + | [Key] | |
| 140 | + | public Guid Id { get; init; } | |
| 141 | + | ||
| 142 | + | // [规则 4.1, 4.2] 必填列:required string, MaxLength, 禁止 [NotNull] | |
| 143 | + | [MaxLength(100)] | |
| 144 | + | public required string Name { get; set; } | |
| 145 | + | ||
| 146 | + | // [规则 4.2, 4.3] 可空列:string?, 注释说明含义, init (如果是创建时不变量) | |
| 147 | + | /// <summary> | |
| 148 | + | /// 描述信息。 | |
| 149 | + | /// 若为空,表示用户在创建时未填写备注。 | |
| 150 | + | /// </summary> | |
| 151 | + | [MaxLength(1000)] | |
| 152 | + | public string? Description { get; init; } | |
| 153 | + | ||
| 154 | + | // [规则 4.3] 系统字段 | |
| 155 | + | public DateTime CreationTime { get; init; } = DateTime.UtcNow; | |
| 156 | + | ||
| 157 | + | // ================= 关联关系 ================= | |
| 158 | + | ||
| 159 | + | // [规则 2.2] 外键ID:required (即使是 Guid? 也要 required 以强制显式赋值) | |
| 160 | + | public required Guid ParentId { get; set; } | |
| 161 | + | ||
| 162 | + | // [规则 2.3, 2.4, 2.5, 2.6] | |
| 163 | + | // 导航引用:Type?, JsonIgnore, ForeignKey | |
| 164 | + | // 严禁 virtual (禁用延迟加载) | |
| 165 | + | // 严禁 [NotNull] (避免编译器歧义) | |
| 166 | + | [JsonIgnore] | |
| 167 | + | [ForeignKey(nameof(ParentId))] | |
| 168 | + | public ParentEntity? Parent { get; init; } | |
| 169 | + | ||
| 170 | + | // [规则 3.1, 3.2, 3.3] | |
| 171 | + | // 集合:IEnumerable (独裁模式), InverseProperty, new List() | |
| 172 | + | // 严禁 virtual | |
| 173 | + | [InverseProperty(nameof(ChildEntity.Parent))] | |
| 174 | + | public IEnumerable<ChildEntity> Children { get; init; } = new List<ChildEntity>(); | |
| 175 | + | } | |
| 176 | + | ``` | |
| 177 | + | ||
| 178 | + | 下面,你正在修正项目的代码,确保其符合严格的实体建模规范。 | |
| 179 | + | ||
| 180 | + | 你需要小心谨慎的梳理实体,考虑已经存住于数据库中的数据,修改时要避免break已有的业务逻辑或API。仔细检查一些东西是不是by design或必须打破军规的。 | |
| 181 | + | ||
| 182 | + | 如果你发现明显的疏漏、错误,你可以去修正实体类。务必小心!实体类是整个项目的最底层,直接修正可能牵一发动全身,小心行事! | |
| 183 | + | ||
| 184 | + | 如果你完成了变更,你必须对每种数据库类型都创建 migration。例如: | |
| 185 | + | ||
| 186 | + | ```bash | |
| 187 | + | cd ./src/MyOrg.MarkToHtml.Sqlite/ | |
| 188 | + | dotnet ef migrations add AddMarkdownDocumentsTable --context "SqliteContext" -s ../MyOrg.MarkToHtml/MyOrg.MarkToHtml.csproj | |
| 189 | + | cd .. | |
| 190 | + | ``` | |
| 191 | + | ||
| 192 | + | 为 MySQl 创建迁移的时候,必须先改好 appsettings.json: | |
| 193 | + | ||
| 194 | + | ```json | |
| 195 | + | { | |
| 196 | + | "ConnectionStrings": { | |
| 197 | + | "AllowCache": "True", | |
| 198 | + | "DbType": "MySql", | |
| 199 | + | "DefaultConnection": "Server=localhost;Database=template;Uid=template;Pwd=template_password;" | |
| 200 | + | }, | |
| 201 | + | // ... other settings ... | |
| 202 | + | } | |
| 203 | + | ``` | |
| 204 | + | ||
| 205 | + | 启动容器: | |
| 206 | + | ||
| 207 | + | ```bash | |
| 208 | + | sudo docker run -d --name db -e MYSQL_RANDOM_ROOT_PASSWORD=true -e MYSQL_DATABASE=template -e MYSQL_USER=template -e MYSQL_PASSWORD=template_password -p 3306:3306 mysql | |
| 209 | + | ``` | |
| 210 | + | ||
| 211 | + | 然后才能 | |
| 212 | + | ||
| 213 | + | ```bash | |
| 214 | + | cd ./src/MyOrg.MarkToHtml.MySql/ | |
| 215 | + | dotnet ef migrations add AddMarkdownDocumentsTable --context "MySqlContext" -s ../MyOrg.MarkToHtml/MyOrg.MarkToHtml.csproj | |
| 216 | + | ``` | |
| 217 | + | ||
| 218 | + | 小心操作!现在开始扮演你的实体判官,去修正当前项目吧! | |
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
2 files changed, 2 insertions, 2 deletions
Fix_lint.md
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # 修Lint | |
| 1 | + | # 修Lint炸 | |
| 2 | 2 | ||
| 3 | 3 | 这是一个 .NET 仓库,它使用了 jb 来进行lint。当然,你可以直接运行其根目录下的 lint.sh 文件,以得到lint的结论。它可能无法通过lint。你会从STDOUT里阅读出无法通过的理由。 | |
| 4 | 4 | ||
fix_tests.md
| @@ -1,4 +1,4 @@ | |||
| 1 | - | # 加测试 | |
| 1 | + | # 修测试炸 | |
| 2 | 2 | ||
| 3 | 3 | 这个仓库是一个 .NET 仓库。显然你不难理解它的项目结构,你可以先简单阅读一下它的代码。 | |
| 4 | 4 | ||
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
1 file changed, 17 insertions
fix_tests.md(file creato)
| @@ -0,0 +1,17 @@ | |||
| 1 | + | # 加测试 | |
| 2 | + | ||
| 3 | + | 这个仓库是一个 .NET 仓库。显然你不难理解它的项目结构,你可以先简单阅读一下它的代码。 | |
| 4 | + | ||
| 5 | + | 它已经有完善的入口点、测试项目等。但是,目前社区普遍批评这个仓库的代码测试通过率很低,经常测试失败。 | |
| 6 | + | ||
| 7 | + | 现在你需要扮演一位高级 .NET 专家,试图判断代码测试失败时,是测试用例的问题,还是环境的问题,还是真的发现了业务逻辑错误。 | |
| 8 | + | ||
| 9 | + | 注意,你没必要mock很多东西,例如数据库、文件系统等。它们在测试环境完全准备的也非常妥当了。你的测试大可以直接作为集成测试,也就是直接启动Web项目,调用它的API,检查返回效果。 | |
| 10 | + | ||
| 11 | + | 被测试的项目中的用例,例如对外部仓库的访问,你也大可以直接让它真的去访问即可。除非是非常昂贵的请求,我们需要Mock。 | |
| 12 | + | ||
| 13 | + | Mock的方法就是在TestStartup里,对一个Service进行继承,override掉老方法,然后再把新服务替代老服务注册回去。 | |
| 14 | + | ||
| 15 | + | 这样整个测试结构非常清晰。现在,你来试图开始运行 dotnet test 得到测试结果,然后分析测试内容,讨论为什么失败,判断是真的bug还是用例问题,尝试缓解。 | |
| 16 | + | ||
| 17 | + | 在写完测试以后,你可以使用 dotnet test 命令来执行你的测试。如果执行失败,除非你非常确定是真的源码业务逻辑有错误,否则不要修改源码。 | |
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
1 file changed, 3 insertions, 1 deletion
Add_tests.md
| @@ -14,4 +14,6 @@ Mock的方法就是在TestStartup里,对一个Service进行继承,override | |||
| 14 | 14 | ||
| 15 | 15 | 这样整个测试结构非常清晰。现在,你来试图开始增加几个新的测试类,覆盖一些核心功能吧! | |
| 16 | 16 | ||
| 17 | - | 如果老的代码实在有函数很难覆盖,考虑增加 [ExcludeFromCodeCoverage] 吧! | |
| 17 | + | 如果老的代码实在有函数很难覆盖,考虑增加 [ExcludeFromCodeCoverage] 吧! | |
| 18 | + | ||
| 19 | + | 在写完测试以后,你可以使用 dotnet test 命令来执行你的测试。如果执行失败,除非你非常确定是真的源码业务逻辑有错误,否则不要修改源码。 | |
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
2 files changed, 4 insertions
Add_tests.md
| @@ -1,3 +1,5 @@ | |||
| 1 | + | # 加测试 | |
| 2 | + | ||
| 1 | 3 | 这个仓库是一个 .NET 仓库。显然你不难理解它的项目结构,你可以先简单阅读一下它的代码。 | |
| 2 | 4 | ||
| 3 | 5 | 它已经有完善的入口点、测试项目等。但是,目前社区普遍批评这个仓库的代码覆盖率很低。尤其是很多核心业务并没有被测试覆盖。 | |
Fix_lint.md
| @@ -1,3 +1,5 @@ | |||
| 1 | + | # 修Lint | |
| 2 | + | ||
| 1 | 3 | 这是一个 .NET 仓库,它使用了 jb 来进行lint。当然,你可以直接运行其根目录下的 lint.sh 文件,以得到lint的结论。它可能无法通过lint。你会从STDOUT里阅读出无法通过的理由。 | |
| 2 | 4 | ||
| 3 | 5 | 你的工作就是:首先分析他为什么无法通过lint,然后试图尽可能不break业务逻辑的修正lint的脚本指出的仓库的问题,也就是去修改 C#,然后重复运行 lint.sh 直到全部通过。 | |
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
1 file changed, 15 insertions
Add_tests.md(file creato)
| @@ -0,0 +1,15 @@ | |||
| 1 | + | 这个仓库是一个 .NET 仓库。显然你不难理解它的项目结构,你可以先简单阅读一下它的代码。 | |
| 2 | + | ||
| 3 | + | 它已经有完善的入口点、测试项目等。但是,目前社区普遍批评这个仓库的代码覆盖率很低。尤其是很多核心业务并没有被测试覆盖。 | |
| 4 | + | ||
| 5 | + | 现在你需要扮演一位高级 .NET 专家,试图提高这个仓库的代码覆盖率。你可以直接去测试项目里增加新的 UT 。 | |
| 6 | + | ||
| 7 | + | 注意,你没必要mock很多东西,例如数据库、文件系统等。它们在测试环境完全准备的也非常妥当了。你的测试大可以直接作为集成测试,也就是直接启动Web项目,调用它的API,检查返回效果。 | |
| 8 | + | ||
| 9 | + | 被测试的项目中的用例,例如对外部仓库的访问,你也大可以直接让它真的去访问即可。除非是非常昂贵的请求,我们需要Mock。 | |
| 10 | + | ||
| 11 | + | Mock的方法就是在TestStartup里,对一个Service进行继承,override掉老方法,然后再把新服务替代老服务注册回去。 | |
| 12 | + | ||
| 13 | + | 这样整个测试结构非常清晰。现在,你来试图开始增加几个新的测试类,覆盖一些核心功能吧! | |
| 14 | + | ||
| 15 | + | 如果老的代码实在有函数很难覆盖,考虑增加 [ExcludeFromCodeCoverage] 吧! | |
anduin ha revisionato questo gist 2 weeks ago. Vai alla revisione
1 file changed, 1 insertion, 1 deletion
Fix_lint.md
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | 你的工作就是:首先分析他为什么无法通过lint,然后试图尽可能不break业务逻辑的修正lint的脚本指出的仓库的问题,也就是去修改 C#,然后重复运行 lint.sh 直到全部通过。 | |
| 4 | 4 | ||
| 5 | - | 你不得修改 lint.sh 文件也不必要分析它!因为这个文件是用来lint的。如果它发现了错误,一定是仓库的错误,不是 linter 的!直接执行即可。 | |
| 5 | + | 你不得修改 lint.sh 文件也不必要分析它!因为这个文件是用来lint的。如果它发现了错误,一定是仓库的错误,不是 linter 的!直接执行即可得到仓库本身的lint错误信息。 | |
| 6 | 6 | ||
| 7 | 7 | 当然,最好你也跑一次 dotnet build 和 dotnet test,确保可以编译、UT全过。 | |
| 8 | 8 | ||