Part 7 · Environment & Tests · Intermediate
Base Test Pattern: One Contract for Every Scenario
Core base_test skeleton that centralizes env creation, default configuration, and lifecycle policy for all derived tests.
Canonical base_test contract
Base tests should define constructor, build_phase, configure hook, and run policy once, then expose narrow extension points.
[TEST][UVM] base_test responsibilities
build_phase:
create cfg
apply defaults
push cfg to config_db
create env
run_phase:
open objection
execute scenario flow
enforce end criteria
close objectionclass base_test extends uvm_test;
`uvm_component_utils(base_test)
my_env env;
env_cfg cfg;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void apply_defaults();
cfg.enable_scoreboard = 1;
cfg.enable_coverage = 1;
cfg.default_timeout_ns = 2_000_000;
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
cfg = env_cfg::type_id::create("cfg");
apply_defaults();
uvm_config_db#(env_cfg)::set(this, "env*", "cfg", cfg);
env = my_env::type_id::create("env", this);
endfunction
endclassKey takeaways
A minimal stable contract beats many ad-hoc convenience methods.
apply_defaults() should express policy, not scenario specifics.
Every derived test should compile if only configure hooks change.
Common pitfalls
Creating env before pushing cfg into config_db.
Using plusarg parsing directly in every child test.
Hardcoding sequence types in base_test without extension hooks.
Implementation pattern kit
The snippets below form a reusable scaffold for teams that maintain large test libraries.
Hook structure
virtual function void configure_scenario();
// child tests override this method to set cfg knobs
endfunction
virtual task pre_main();
// optional setup common to all tests
endtask
virtual task post_main();
// optional teardown common to all tests
endtask[TEST][ENV] hook order
build_phase:
apply_defaults()
configure_from_plusargs()
create env
run_phase:
pre_main()
configure_scenario()
run_main_sequence()
post_main()Review checklist
No direct hierarchical signal peek/poke in test classes.
No duplicated timeout logic outside base_test.
Factory override calls are centralized and auditable.
All child tests override only documented hooks.
[TEST] quick smoke
1) run base_test directly
2) run one child scenario
3) confirm both share same startup logs
4) verify only scenario knobs differCommon pitfalls
Adding too many hooks without clear ownership semantics.
Allowing configure methods to create components dynamically.
Hiding mandatory lifecycle in optional hooks.