为了避免贫血模型,在封装领域逻辑时,考虑设计要素的顺序为:
Value Object -> Entity -> Domain Service


源账户开始交易
源账户验证可用资金(请注意,这必须在交易内部完成,以避免干预提款!)
源账户减少自身余额
源帐户请求目标帐户增加其余额
源账户更新其日志以指出这是一次转账(而不是,例如,简单的提款)
源帐户请求目标帐户更新其日志
源账户结束交易
源账户通知账户持有人转账成功
代码实现的行为如下:
template <class ConcreteAccountType>
class TransferMoneySourceAccount: public MoneySource
{
private:
ConcreteDerived *const self() {
return static_cast<ConcreteDerived*>(this);
}
void transferTo(Currency amount) {
// This code is reviewable and
// meaningfully testable with stubs!
beginTransaction();
if (self()->availableBalance() < amount) {
endTransaction();
throw InsufficientFunds();
} else {
self()->decreaseBalance(amount);
recipient()->increaseBalance (amount);
self()->updateLog("Transfer Out", DateTime(), amount);
recipient()->updateLog("Transfer In", DateTime(), amount);
}
gui->displayScreen(SUCCESS_DEPOSIT_SCREEN);
endTransaction();
}
}


我们进行Linux后端服务编程时,经常会进行系统操作。Golang对系统命令执行的支持是 通过os.exec库实现的。
那么在测试时,我们如何在UT当中,对调用os.exec的地方进行单元测试呢?
这个库的mock并不好处理,在设置exec.Command之后执行它的run方法,所以即使想对它 进行打桩也找不到可以替换的接口和mock对象。
在exec这个包的测试文件exec_test.go中,可以看到有这样一段代码: ```Go func helperCommand(t *testing.T, s ...string) *exec.Cmd {
cs := []string{"-test.run=TestHelperProcess", "--"}
cs = append(cs, s...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd } ``` 上面这个函数在测试用例执行时执行的,并且在函数中执行一个命令为os.Args[0]的系统命令。 os.Args[0]就是这个Go代码在执行go test的时候的执行程序。 通过上面这个代码,go test执行到这个函数时,就会以```"-test.run=TestHelperProcess -- "``` 为参数执行go test执行程序。go test的可执行文件,执行机制是执行 test.run指定的测试用例。 那么现在就相当与指定执行```TestHelperProcess```这个用例了。 详细解释见:[测试 os/exec.Command](https://npf.io/2015/06/testing-exec-command/)
var execCommand = exec.Commandfunc fakeExecCommand(command string, args...string) *exec.Cmd {
cs := []string{"-test.run=TestHelperProcess", "--", command}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}
func TestHelperProcess(t *testing.T){
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
cmdName = os.Args[3] // 0--是 go test 执行程序 1--是 TestHelperProcess 2-- 是“--”
fmt.Fprintf(os.Stdout, "command name is %s\n ")
os.Exit(0)
}
execCommand = fakeExecCommand
defer func(){ execCommand = exec.Command }()
这个方法相对优雅,而且可以进行针对不同命令进扩展,以及统计等。