How to convince Oracle to create optimal execution plans in SQL queries with bind variables?
Test 1 is logically incorrect for this use-case. If you provide NULL as the value of :B1, it will return 0 rows.
Test 2 is logically correct, but its execution plan is suboptimal, so is performance. In this case the optimizer is unable to utilize the index, because NULL values are not indexed by default.
Test 3 is logically correct, and its execution plan is optimal, and this should be used for a simple case like this. But be very careful with this.
employee_id = NVL(:B1,employee_id) was transformed into a union of two branches collecting results based on  employee_id = :B1 and :B1 is null. This is called NVL optimization and is displayed as an OR-expansion (ORE) in the execution plan. This is a nice little trick of the optimizer.
The problem is, this works properly only when you have 1 predicate like that. If you have more predicates like that, the optimizer will not do this trick for the remaining predicates, and it will not created additional branches for the UNION.
A query like this:
SELECT * FROM MY_TABLE
WHERE COL1 = NVL(:B1, COL1) AND COL2 = NVL(:B2, COL2) AND COL3 = NVL(:B3, COL3);

will not produce branches for all path. The optimizer will perform the NVL optimization for 1 predicate, then it will place the remaining predicates in the 2 branches created by the transformation of the first predicate. This means a plan generated from a query like that will never be optimal. This is something that causes a big share of SQL performance problems I encounter, and a not a single developer was aware of this so far. If you want to have optimal execution plans and performance, you should generate the simplest SQL statement on the fly instead of trying to cover all possible scenarios with a single SQL statement. For example, if there was no value provided for :B1, then simply do not include that predicate in the SQL statement.