A traditional TestFixture might look something like this
1: [TestFixture]
2: public class EmployeeRequests_Tests
3: {
4: private EmployeeRequests target;
5: private Mock<IEmployeeRepository> MockEmployeeRepository;
6: [SetUp]
7: public void Setup()
8: {
9: target = new EmployeeRequests();
10: MockEmployeeRepository = new Mock<IEmployeeRepository>();
11: MockEmployeeRepository.Setup(x =>
12: x.GetEmployeeByCityState(It.IsAny<string>(), It.IsAny<string>()))
13: .Returns(new List<EmployeeDvr>
14: {
15: new EmployeeDvr {City = "Seattle", State = "Washington", EmployeeName = "bob"}
16: });
17: target.EmployeeRepository = MockEmployeeRepository.Object;
18: }
19:
20: [Test]
21: public void ReturnsErrorForEmptyCity_Test()
22: {
23: target.GetEmployeeByLocation(string.Empty, "Washington");
24: var actual = target.Errors.Where(x => x.Contains("Invalid City"));
25: Assert.AreEqual(1,actual.Count());
26: }
27:
28: [Test]
29: public void ReturnsErrorForEmptyState_Test()
30: {
31: target.GetEmployeeByLocation("Seattle", string.Empty);
32: var actual = target.Errors.Where(x => x.Contains("Invalid State"));
33: Assert.AreEqual(1, actual.Count());
34: }
35:
36: [Test]
37: public void VailidCityStateCallRepository_Test()
38: {
39: target.GetEmployeeByLocation("Seattle", "Washington");
40: MockEmployeeRepository.Verify(x => x.GetEmployeeByCityState("Seattle", "Washington"));
41: }
42: }
When doing behavior testing the idea is to test a code path for a specific context vs. testing for all possibilities a method could result in, basically breaking it down into much easier to read parts.
To do this we set up a base or abstract class that has a context method for setting up your test and a because method for running it, here is an example of one using generics
1: [TestFixture]
2: public abstract class Test_Context<T> where T : new()
3: {
4: public T Target;
5:
6:
7: [SetUp]
8: public void Setup()
9: {
10: Context();
11: Because();
12: }
13:
14: public virtual void Context()
15: {
16: Target = new T();
17: }
18:
19: public abstract void Because();
20: }
1: [TestFixture]
2: public class When_Requesting_Employees_From_Seattle_Washington_Tests : Test_Context<EmployeeRequests>
3: {
4: public Mock<IEmployeeRepository> MockEmployeeRepository;
5:
6: public override void Context()
7: {
8: base.Context();
9: MockEmployeeRepository = new Mock<IEmployeeRepository>();
10: MockEmployeeRepository.Setup(x => x.GetEmployeeByCityState("Seattle", "Washington"))
11: .Returns(new List<EmployeeDvr> { new EmployeeDvr { City = "Seattle", State = "Washington" } });
12: Target.EmployeeRepository = MockEmployeeRepository.Object;
13: }
14:
15: public override void Because()
16: {
17: Target.GetEmployeeByLocation("Seattle", "Washington");
18: }
19:
20: [Test]
21: public void HasNoErrors_Test()
22: {
23: Assert.AreEqual(0,Target.Errors.Count);
24: }
25:
26: [Test]
27: public void Calls_The_Repository()
28: {
29: MockEmployeeRepository.Verify(x=>x.GetEmployeeByCityState("Seattle","Washington"),Times.Once());
30: }
31: }
The next thing you may notice is we don’t have as many test methods, sense we are only testing for what happens when we are getting employees from Seattle, WA. that’s all we are testing for. This may produce more test textures classes but at the same the test fixtures are more targeted and give a better description of what you are testing making it easier to read/maintain later and is sometimes called executable specifications, where you can find out the intended function of a class or method simply by reading then names for the test fixture and tests. For more examples of unit tests vs. behavoir tests see the sample application in the sample code repository.
1 comment:
Post a Comment