V = { v in H^1(Omega) : v = u_D on the boundary},
V’ = { v in H^1(Omega): v = 0 on the boundary}.
The line
bc = DirichletBC(V, u_D, boundary)
ensures that our solution u fullfills u = u_D. However I don’t understand why there isn’t any code to guarantee that the functions from the test space obey v = 0.
A = assemble(A)
b = assemble(L)
bc.apply(A, b)
solve(A, u.vector(), b)
the call with bc.apply will enforce at the same time that u = u_D and v = 0 on that boundary. you do not see this snippet in the tutorial you linked because it happens internally at solve(a == L, w, bc).