使用Go测试数据密集型代码,第4部分

总览

这是关于使用Go测试数据密集型代码的教程系列中的五分之四。 在第三部分中,我介绍了对包含关系数据库和Redis缓存的本地复杂数据层的测试。

在本教程中,我将使用共享测试数据库,使用生产数据快照以及生成自己的测试数据来对远程数据存储进行测试。

针对远程数据存储进行测试

到目前为止,我们所有的测试都是在本地进行的。 有时,这还不够。 您可能需要针对难以在本地生成或获取的数据进行测试。 测试数据可能非常大或经常更改(例如,生产数据快照)。

在这种情况下,对于每个开发人员而言,将最新的测试数据复制到他们的计算机上可能会太慢且太昂贵。 有时,测试数据很敏感,尤其是远程开发人员不应该将其存储在笔记本电脑上。

这里有几个选项可供考虑。 您可以在不同情况下使用这些选项中的一个或多个。

共享测试数据库

这是一个非常常见的选择。 有一个共享的测试数据库,所有开发人员都可以连接并对其进行测试。 该共享测试数据库作为共享资源进行管理,并且经常定期填充一些基准数据,然后开发人员可以针对该数据库运行测试以查询现有数据。 他们还可以创建,更新和删除自己的测试数据。

在这种情况下,您需要大量的纪律和适当的流程。 如果两个开发人员在同一时间运行相同的测试以创建和删除相同的对象,则两个测试都将失败。 请注意,即使您是唯一的开发人员,并且其中一个测试无法正常进行自我清理,您的下一个测试也可能会失败,因为数据库现在具有来自先前测试的一些额外数据,这些数据可能会破坏当前测试。

远程运行测试

这就是CI / CD管道甚至自动构建系统的工作方式。 开发人员进行更改,然后自动构建和测试开始运行。 但是您也可以只连接到具有您的代码的远程计算机,然后在其中运行测试。

好处是您可以复制精确的本地设置,但可以访问远程环境中已经可用的数据。 缺点是您不能使用自己喜欢的工具进行调试。

临时远程测试实例

启动远程临时测试实例可确保您仍然与其他开发人员隔离。 从概念上讲,它与运行本地实例非常相似。 您仍然需要启动一个或多个数据存储。 您仍然需要(远程)填充它们。 但是,您的测试代码在本地运行,您可以使用自己喜欢的IDE(在我的情况下为Gogland)进行调试和故障排除。 如果开发人员在测试完成后仍保持测试实例的运行,则可能很难进行操作管理。

使用生产数据快照

使用共享测试数据存储时,通常会在其中填充生产数据快照。 根据数据的敏感程度和关键程度,以下一些利弊可能是相关的。

使用生产数据进行测试的利弊

优点:

  • 您针对真实数据进行测试。 如果可行,那就很好。
  • 您可以加载和性能测试数据,它们代表实际的负载。
  • 您无需编写试图模拟实际生产数据的数据生成器。

缺点:

  • 测试错误条件可能并不容易。
  • 生产数据可能很敏感,需要特殊处理。
  • 您需要编写一些代码或定期手动同步快照。
  • 您必须处理格式或架构更改。
  • 很难区分出生产数据混乱的问题。

匿名生产数据

好。 您已经取得了飞跃,并决定使用生产数据快照。 如果您的数据涉及任何形状或形式的人类,则可能必须匿名化数据。 这是非常困难的。

您不能只替换所有名称并完成它。 有许多方法可以从严重匿名的数据快照中恢复PII(个人身份信息)和PHI(受保护的健康信息)。 如果您感到好奇,请查看Wikipedia作为起点。

我在Helix工作,我们在Helix开发了一个个人基因组学平台,该平台处理最私人的数据-人的测序DNA。 我们为防止意外(和恶意)数据泄露提供了一些严格的保护措施。

更新测试和数据快照

使用生产数据快照时,必须定期刷新快照以及相应的测试。 时机由您决定,但一定要在架构或格式更改时执行。

理想情况下,您的测试不应测试特定快照的属性。 例如,如果您每天刷新快照,并且您有一个验证快照中记录数的测试,那么您就必须每天更新此测试。 最好以更通用的方式编写测试,因此仅在被测代码更改时才需要更新它们。

生成测试数据

另一种方法是生成您自己的测试数据。 优点和缺点与使用生产数据快照完全相反。 请注意,您还可以将两种方法结合起来,对生产数据快照运行一些测试,并使用生成的数据运行其他测试。

随机测试数据生成

您将如何生成测试数据? 您可以疯狂使用完全随机的数据。 例如,对于Songify,我们可以为用户电子邮件,URL,描述和标签生成完全随机的字符串。 结果将是混乱的,但是有效的数据,因为Songify不执行任何数据验证。

这是一个用于生成随机字符串的简单函数:

func makeRandomString(length int) string {
    const bytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"    
	randBytes := make([]byte, length)
	for i := 0; i < length; i++ {
		b := bytes[rand.Intn(len(bytes))]
		randBytes[i] = b
	}
	return string(randBytes)
}

让我们编写一个函数,该函数添加五个随机用户,然后添加在五个用户之间随机分配的100首随机歌曲。 我们必须吸引用户,因为歌曲并非一帆风顺。 每首歌曲始终与至少一个用户相关联。

func (m *InMemoryDataLayer) PopulateWithRandomData() {
    users := []User{}
	// Create 5 users
	for i := 0; i < 5; i++ {
		name := makeRandomString(15)
		u := User{
			Email: name + "@" + makeRandomString(12) + ".com",
			Name:  makeRandomString(17),
		}
		m.CreateUser(u)
		users = append(users, u)
	}

	// Create 100 songs and associate randomly with 
    // one of the 5 users
	for i := 0; i < 100; i++ {
		user := users[rand.Intn(len(users))]
		song := Song{
			Url: fmt.Sprintf("http://www.%s.com", 
                             makeRandomString(13)),
			Name: makeRandomString(16),
		}
		m.AddSong(user, song, []Label{})
	}
}

现在,我们可以编写一些可以处理大量数据的测试。 例如,这是一项测试,验证我们可以在一次通话中获得全部100首歌曲。 请注意,测试在进行调用之前先调用PopulateWithRandomData()

func TestGetSongs(t *testing.T) {
    dl, err := NewInMemoryDataLayer()
	if err != nil {
		t.Error("Failed to create in-memory data layer")
	}

	dl.PopulateWithRandomData()

	songs, err := dl.GetSongs()
	if err != nil {
		t.Error("Failed to create in-memory data layer")
	}

	if len(songs) != 100 {
		t.Error(`GetSongs() didn't return 
                 the correct number of songs`)
	}
}

基于规则的测试数据生成

通常,完全随机的数据是不可接受的。 每个数据存储都必须遵守一些约束,并且必须遵循复杂的关系才能创建系统可以操作的有效数据。 您可能还想生成一些无效数据来测试系统如何处理它们,但是这些将是您将要注入的特定错误。

该方法将类似于随机数据生成,只是您将拥有更多逻辑来强制执行规则。

例如,假设我们要执行一个规则,即用户最多可以拥有30首歌曲。 我们可以决定每个用户恰好拥有20首歌曲,而不是随机创建100首歌曲并将其分配给用户,或者可以创建一个没有歌曲的用户,以及另外四个每个拥有25首歌曲的用户。

基于叙述的测试数据生成

在某些情况下,生成测试数据非常复杂。 我最近在一个项目中工作,该项目必须将测试数据注入到四个不同的微服务中,每个服务都管理自己的数据库,每个数据库中的数据与其他数据库中的数据相关。 要使所有内容保持同步非常具有挑战性并且需要大量劳动。

通常,在这种情况下,使用系统API和创建数据的现有工具会更容易,而不是直接进入多个数据存储区并祈求您不要破坏Universe的结构。 我们之所以无法采用这种方法,是因为我们实际上需要故意创建一些无效数据来测试各种错误情况,并跳过正常工作流程中发生的与外部系统有关的一些副作用。

结论

在本教程中,我们介绍了针对远程数据存储的测试,使用共享的测试数据库,使用生产数据快照以及生成自己的测试数据。

在第五部分中,我们将着重于模糊测试,测试缓存,测试数据完整性,测试幂等和丢失数据。 敬请关注。

翻译自: https://code.tutsplus.com/tutorials/testing-data-intensive-code-with-go-part-4--cms-29851

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值