测试有状态 类
Testing stateful classes
我有一个class可以加载图像,关闭它或重命名它(至少现在,将来会有更多对打开文件的操作)。
我已经用 Facade 模式实现了它(我认为是这样)。
当我尝试为其编写测试时,我很快注意到我需要使用不同的先决条件(例如,在调用某些方法之前加载图像时)对其进行测试。而且测试量很大,当我添加新的操作时会增长得非常快。据我了解,这些不是单元测试,而是端到端测试。
我是 TDD 的新手,所以谁能告诉我进行如此复杂的测试是正常的做法吗?
我期待关于我的 class 责任太多的答案,但让我们考虑一下替代方案:
// pseudo-code
// #1 - my way
function onRenameButtonClick(newName) {
imageFacade.rename(newName)
}
// #2 - the other way
function onRenameButtonClick(newName) {
if (imageController.isOpened()) {
imageRenamer.rename(imageController.image, newName)
}
}
最后我仍然需要测试 #2 的正确行为,它仍然会涉及使用不同的先决条件。
你是怎么处理这种情况的?这是正常现象还是我做错了什么?
P. S. 这是我的门面 class 的骨架,请注意 config
中有纯函数,它们可以执行实际工作,并且 actions
正在拼接这些纯函数并根据状态。
function makeImageWTF(loader, renamer) {
return {
state: {
image: null,
filePath: null,
isLoading: false
},
config: {
loader,
renamer
},
triggers: {
opening: new Bus(),
opened: new Bus(),
closed: new Bus(),
renamed: new Bus(),
error: new Bus()
},
actions: {
open: function(path) {},
close: function() {},
rename: function(newName) {}
}
}
}
这里是测试框架
describe('ImageWTF', function() {
describe('initial state', function() {
it('should be that', function() {
var wtf = makeImageWTF()
assert.deepEqual({
image: null,
filePath: null,
isLoading: false,
}, wtf.state)
})
})
describe('#open(path)', function() {
it('sets isLoading')
it('calls config.loader with path')
it('sets image, fileName')
it('triggers loading, loaded or error')
it('clears isLoading')
})
describe('#close()', function() {
describe('when image is opened', function() {
it('resets image')
it('triggers closed')
})
describe('when image is NOT opened', function() {
it('triggers error')
})
})
describe('#rename()', function() {
describe('when image is opened', function() {
it('calls config.renamer with oldName and newName')
it('sets fileName')
it('triggers renamed')
})
describe('when image is NOT opened', function() {
it('triggers error')
})
})
})
(Unit) 测试就像生产代码一样——它们可能会变得复杂。但目标当然应该是让它们尽可能简单。
如果您还没有对您的代码进行测试,我建议您开始编写测试以涵盖最重要的用例。一旦你让那些工作到位,你就可以重构它们。但对我来说,主要重点是进行适当的测试,沿着这条路你会学到很多东西。
如果它们不是"unit test",我不会太在意,改变它们以适合您的应用程序。
尽量不要将测试与生产代码紧密结合,因为您希望在不更改测试的情况下灵活地重构生产代码(当然,反之亦然)。对我来说,单元测试实际上是让更改和重构代码变得简单快捷。它们不会捕获您应用程序中的所有错误或行为 - 为此您还需要关注其他类型的测试。
我有一个class可以加载图像,关闭它或重命名它(至少现在,将来会有更多对打开文件的操作)。
我已经用 Facade 模式实现了它(我认为是这样)。
当我尝试为其编写测试时,我很快注意到我需要使用不同的先决条件(例如,在调用某些方法之前加载图像时)对其进行测试。而且测试量很大,当我添加新的操作时会增长得非常快。据我了解,这些不是单元测试,而是端到端测试。
我是 TDD 的新手,所以谁能告诉我进行如此复杂的测试是正常的做法吗?
我期待关于我的 class 责任太多的答案,但让我们考虑一下替代方案:
// pseudo-code
// #1 - my way
function onRenameButtonClick(newName) {
imageFacade.rename(newName)
}
// #2 - the other way
function onRenameButtonClick(newName) {
if (imageController.isOpened()) {
imageRenamer.rename(imageController.image, newName)
}
}
最后我仍然需要测试 #2 的正确行为,它仍然会涉及使用不同的先决条件。
你是怎么处理这种情况的?这是正常现象还是我做错了什么?
P. S. 这是我的门面 class 的骨架,请注意 config
中有纯函数,它们可以执行实际工作,并且 actions
正在拼接这些纯函数并根据状态。
function makeImageWTF(loader, renamer) {
return {
state: {
image: null,
filePath: null,
isLoading: false
},
config: {
loader,
renamer
},
triggers: {
opening: new Bus(),
opened: new Bus(),
closed: new Bus(),
renamed: new Bus(),
error: new Bus()
},
actions: {
open: function(path) {},
close: function() {},
rename: function(newName) {}
}
}
}
这里是测试框架
describe('ImageWTF', function() {
describe('initial state', function() {
it('should be that', function() {
var wtf = makeImageWTF()
assert.deepEqual({
image: null,
filePath: null,
isLoading: false,
}, wtf.state)
})
})
describe('#open(path)', function() {
it('sets isLoading')
it('calls config.loader with path')
it('sets image, fileName')
it('triggers loading, loaded or error')
it('clears isLoading')
})
describe('#close()', function() {
describe('when image is opened', function() {
it('resets image')
it('triggers closed')
})
describe('when image is NOT opened', function() {
it('triggers error')
})
})
describe('#rename()', function() {
describe('when image is opened', function() {
it('calls config.renamer with oldName and newName')
it('sets fileName')
it('triggers renamed')
})
describe('when image is NOT opened', function() {
it('triggers error')
})
})
})
(Unit) 测试就像生产代码一样——它们可能会变得复杂。但目标当然应该是让它们尽可能简单。
如果您还没有对您的代码进行测试,我建议您开始编写测试以涵盖最重要的用例。一旦你让那些工作到位,你就可以重构它们。但对我来说,主要重点是进行适当的测试,沿着这条路你会学到很多东西。
如果它们不是"unit test",我不会太在意,改变它们以适合您的应用程序。
尽量不要将测试与生产代码紧密结合,因为您希望在不更改测试的情况下灵活地重构生产代码(当然,反之亦然)。对我来说,单元测试实际上是让更改和重构代码变得简单快捷。它们不会捕获您应用程序中的所有错误或行为 - 为此您还需要关注其他类型的测试。