The solution at the moment only contains a couple of short tests, that cover the complete cycle of a project and hiring staff. Nevertheless it’s a complete integration test over the whole system covering at least the most important areas of the application.
This is the test – it’s quite self-explaining I believe (this is one of the biggest advantages of this approach – tests are fairly easy to write and to read – and therefore to maintain):
[TestMethod] public void TestBasicProject() { // create system system = new ATBSystem(true); // click on the "Project Offers" button system.MainWindow.ProjectOffersCommand.Execute(null); // make sure that the correct page is displayed Assert.IsTrue(system.MainWindow.CurrentPage is VProjectOffersPage); // get the page VProjectOffersPage projectOffersPage = system.MainWindow.CurrentPage as VProjectOffersPage; // verify that the correct date is displayed Assert.AreEqual("Tuesday, 1 Jan 1980", system.MainWindow.CurrentDate); // get the number of project offers int num = projectOffersPage.Projects.Count; // the last one is a special test project that we use for the test VProject project = projectOffersPage.Projects[num - 1]; // TODO: check project properties // click the "Take" button project.TakeCommand.Execute(null); // there should be one project less now Assert.AreEqual(num - 1, projectOffersPage.Projects.Count); // and on the project page we should have one project now Assert.AreEqual(1, system.MainWindow.ProjectsPage.Projects.Count); // goto to hire page - click on the "Hire Staff" button system.MainWindow.HireStaffCommand.Execute(null); // make sure that the correct page is displayed Assert.IsTrue(system.MainWindow.CurrentPage is VHireStaffPage); // get the page VHireStaffPage hireStaffPage = system.MainWindow.CurrentPage as VHireStaffPage; // get the number of persons int personCount = hireStaffPage.Persons.Count; // the last one is a special test developer that we will use in this test VPerson myDeveloper = hireStaffPage.Persons[personCount - 1]; // click the "Hire" button myDeveloper.HireCommand.Execute(null); // there should be one developer less now Assert.AreEqual(personCount - 1, hireStaffPage.Persons.Count); // and one more on our "Staff" page Assert.AreEqual(1, system.MainWindow.StaffTab.Staff.Count); // goto to project page system.MainWindow.ProjectsCommand.Execute(null); // make sure that the correct page is displayed Assert.IsTrue(system.MainWindow.CurrentPage is VProjectsPage); // get the page VProjectsPage projectsPage = system.MainWindow.CurrentPage as VProjectsPage; // get the first (and only) project view VProject newProject = system.MainWindow.ProjectsPage.Projects[0]; // select our developer; that makes him part of the project newProject.SelectedPerson = 0; // he must be a member now Assert.AreEqual(1, newProject.Members.Count); // get our developer as member VPerson developerAsMember = newProject.Members[0]; // select business part of project for our developer developerAsMember.CurrentProjectTask = 4; // start the sim system.MainWindow.ContinueCommand.Execute(null); // simulate an update system.Update(); // ...
Automation concept
When testing manually, the more complex the application becomes, the longer it takes to click through the application to get to the point where I’d like to test something.
For example: I wanted to test if the message boxes at the end of a project are displayed correctly: the success box when the project was successfully finished, the “too many bugs” box when there were too many bugs for the customer and the “overdue box” when the project took too long. That means that I have to click through the whole procedure of taking the project, switching to the “Hire staff page”, hire the person I want, switch to the project page, select the person to make him a member etc. pp.
All together it takes 2-3 minutes to reproduce all those steps (especially because of waiting for the individual tasks to finish) – again and again. And the more the application grows, the more complex test scenarios you will encounter and the more time development and testing will take.
It would be cool if there were a kind of automation mechanism to play all these pre steps automatically up to that point where I want to test something…
The View Model concept allows it to automate the application fairly easy. It’s basically the same as writing an integration test but just from inside the application itself. Since the complete view model interface to control the application is made public, so the view can operate on it, it’s also possible to automate the application via the same interface from the application itself.
I created an Automator class that runs a test case to automate the application; this for example is the code that simulates the “untested” part of a project: (the _dispatcher is the connection to the main system.)
public void RunUntested() { // click on the "Project Offers" button _dispatcher.MainWindow.ProjectOffersCommand.Execute(null); // the last one is a special test project that we use for the test VProject project = _dispatcher.MainWindow.ProjectOffers.Projects[_dispatcher.MainWindow.ProjectOffers.Projects.Count - 1]; // click the "Take" button project.TakeCommand.Execute(null); // goto to hire page - click on the "Hire Staff" button _dispatcher.MainWindow.HireStaffCommand.Execute(null); // the last one is a special test developer that we will use in this test VPerson person = _dispatcher.MainWindow.PersonOffers.Persons[_dispatcher.MainWindow.PersonOffers.Persons.Count - 1]; // click the "Hire" button person.HireCommand.Execute(null); // goto to project page _dispatcher.MainWindow.ProjectsCommand.Execute(null); // get the first (and only) project project = _dispatcher.MainWindow.ProjectsPage.Projects[0]; // select our developer; that makes him part of the project project.SelectedPerson = 0; // get our developer as member person = project.Members[0]; // select OS part of project for our developer person.CurrentProjectTask = 2; // while we not reached 100 % while (project.CurrOsSkill.AmountAsInt < 100) { // simulate an update _dispatcher.Model.ForcedUpdate(); } // select database part of project for our developer person.CurrentProjectTask = 3; // while we not reached 100 % while (project.CurrDatabaseSkill.Amount < 100) { // simulate an update _dispatcher.Model.ForcedUpdate(); } // select UI part of project for our developer person.CurrentProjectTask = 1; // while we not reached 100 % while (project.CurrUiSkill.AmountAsInt < 100) { // simulate an update _dispatcher.Model.ForcedUpdate(); } // select business part of project for our developer person.CurrentProjectTask = 4; // while we not reached 100 % while (project.CurrBusinessSkill.AmountAsInt < 100) { // simulate an update _dispatcher.Model.ForcedUpdate(); } }
Note: As you can see, the automation code is pretty much the same as the integration test. To be precise: it should be the same code and it will be the same code in the next version. The only difference is, that the integration test needs the initialization of the system which is not required with the Automator because it runs inside the application itself. Furthermore the test uses Asserts, which are useless inside the application.
The funny thing with this approach is, that you can visually follow the automation because the views always represent the current view model state – it’s like a macro.
Running this automation script now takes about a second – in comparison to 2-3 minutes when done manually; that is a quite simple way of saving lots of time – day by day.
Having unit tests does not mean that there are no bugs, so if you encounter some, please let me know.
The game’s main page you can find here: All Those Bugs Page