编程过程有时候就像一场与丧尸群之间的战斗
发表时间:2023-10-10 08:02:58
文章来源:炫佑科技
浏览次数:221
菏泽炫佑科技
编程过程有时候就像一场与丧尸群之间的战斗
很久以前,当我还是一名初出茅庐的程序员时,我们常常被分配大量的工作。 我们每个人都被分配了一项编程任务,然后回到我们的小隔间敲击键盘。 我记得团队成员一次坐在自己的小隔间里几个小时,努力创建一个没有错误的程序。 当时流行的观念是:一次能做的事情越多,你的能力就越强。
对我来说,能够长时间编写或修改代码而不停下来看看它是否有效就像是一种荣誉徽章。 当时,我们都认为停下来检查代码是否有效是无能的表现,只有菜鸟才会这么做。 一个“真正的开发人员”应该能够一次性构建整个程序,而无需停下来检查任何内容!
然而,当我在开发过程中停止测试代码时,结果适得其反,现实检查给了我沉重的打击。 我的代码要么无法编译,要么无法构建,要么无法运行,或者无法按预期处理数据。 我不得不绝望地挣扎去解决这些恼人的问题。
躲避僵尸群
如果旧的工作方式让您感到困惑,那是因为它确实如此。 我们一次性解决所有任务,解决问题堆,结果却制造出更多问题。 这就像与一群僵尸作战。
今天我们学会了避免同时做太多事情。 当我**次听到一些专家称赞避免批量开发的好处时,我发现这有悖常理,但我从过去的错误中吸取了教训。 我使用 James 所说的方法来指导我的软件开发工作。
救援之路!
代表以下缩写词:
我将在本系列文章中对它们进行分析和解释。
*简单的场景
*简单的场景是指*简单的可能情况。
人们倾向于从硬编码值开始,因为这是*简单的方法。 通过在编码活动中使用硬编码值,您可以快速构建具有即时反馈的解决方案。 它不需要几分钟,更不用说几个小时,并且使用硬编码值可以让您立即与您正在构建的系统进行交互。 如果你喜欢这种互动,就继续朝这个方向努力。 如果你发现你不喜欢这种互动编程过程有时候就像一场与丧尸群之间的战斗,你可以轻松地放弃它,并且没有什么可失去的。
本系列文章将以构建一个简单的购物系统的后端API为例介绍。 该服务提供的API允许用户创建购物篮、向购物篮添加商品、从购物篮中删除商品以及计算商品的总价。
首先,创建项目的基本结构(将购物程序代码和测试代码分别放在app和tests目录中)。 我们的示例使用开源 xUnit 测试框架。
现在就卷起袖子,在实践中了解极简场景吧!
[Fact]
public void NewlyCreatedBasketHas0Items {
var expectedNoOfItems = 0;
var actualNoOfItems = 1;
Assert.Equal(expectedNoOfItems, actualNoOfItems);
}
这是一个伪测试,它测试硬编码值。 新创建的购物篮是空的,因此预期购物篮中的商品数量为 0。这种预期通过比较预期值是否等于实际值来表达为测试(或断言)。
运行测试,输出结果如下:
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.57] tests.UnitTest1.NewlyCreatedBasketHas0Items [FAIL]
X tests.UnitTest1.NewlyCreatedBasketHas0Items [4ms]
Error Message:
Assert.Equal Failure
Expected: 0
Actual: 1
[...]
这个测试显然失败了:预期的项目数是 0,但实际值被硬编码为 1。
当然,您可以立即将硬编码值从 1 更改为 0,测试就会通过:
[Fact]
public void NewlyCreatedBasketHas0Items {
var expectedNoOfItems = 0;
var actualNoOfItems = 0;
Assert.Equal(expectedNoOfItems, actualNoOfItems);
}
正如所料,运行测试,测试通过:
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 1.0950 Seconds
您可能认为执行强制失败的测试绝对没有意义,但无论测试多么简单,确保它可失败是绝对必要的。 只有这样软件开发,才能保证在后续的工作中,如果你不小心破坏了程序的处理逻辑,测试能够给你相应的警告。
现在停止伪造数据并用从 API 获取的值替换硬编码值。 现在我们已经构建了一个测试,该测试确实不会预期出现包含 0 件商品的空购物篮,现在是时候编写一些应用程序代码了。
就像常见的软件建模活动一样,我们从构建一个简单的界面开始。 在应用程序目录中创建一个新文件 .cs(接口名称通常以大写 I 开头)。 声明一个在此接口中命名的方法,该方法以 int 类型返回商品的数量。 这是接口的代码:
using System;
namespace app {
public interface IShoppingAPI {
int NoOfItems;
}
}
当然这个接口不能做任何事情,你需要实现它。 在应用程序目录中创建另一个文件。 它将被声明为已实现的公共类。 在类中定义一个返回整数1的方法:
using System;
namespace app {
public class ShoppingAPI : IShoppingAPI {
public int NoOfItems {
return 1;
}
}
}
从上面的代码中,您会发现您通过返回硬编码值 1 来伪造代码逻辑。在现阶段这是一件好事,因为您需要保持一切都超级简单。 现在还不是仔细思考如何实现购物篮的处理逻辑的时候。 我们稍后会这样做! 到目前为止,您刚刚构建了一个极简场景,看看您是否对设计感到满意。
要确定这一点,请将硬编码值替换为该 API 在运行时请求时应返回的值。 你需要通过using app告诉测试你使用的购物逻辑代码在哪里; 陈述。
接下来,您需要实例化该接口:
IShoppingAPI shoppingAPI = new ShoppingAPI;
该实例用于发送请求和接收返回值。
现在,代码变成这样:
using System;
using Xunit;
using app;
namespace tests {
public class ShoppingAPITests {
IShoppingAPI shoppingAPI = [new][3] ShoppingAPI;
[Fact]
public void NewlyCreatedBasketHas0Items {
var expectedNoOfItems = 0;
var actualNoOfItems = shoppingAPI.NoOfItems;
Assert.Equal(expectedNoOfItems, actualNoOfItems);
}
}
}
显然,执行此测试失败,因为您硬编码了不正确的返回值(预期值为 0,但返回值为 1)。
同样,您可以通过将硬编码值从 1 更改为 0 来使测试通过,但现在这样做是浪费时间。 既然设计的界面已经与测试相关联,您剩下的责任就是编写代码来实现预期的行为逻辑。
编写应用程序代码时,您必须决定用于表示购物篮的数据结构。 为了保持设计简单,请尝试选择*简单的类型来表示 C# 中的集合。 这是我首先想到的事情。 它非常适合当前的使用场景——可以保存无限数量的元素并且易于遍历和访问。
因为它是 . 包,需要在代码中声明:
using System.Collections;
那么声明就变成这样:
ArrayList basket = new ArrayList;
*后,将编码值替换为实际代码:
public int NoOfItems {
return basket.Count;
}
此测试通过,因为购物篮*初是空的并且 .Count 返回 0。
这也是您的**个极简场景测试将要做的事情。