FitNesse Application for .Net Applications

Hi, Habr.

I think many of you have heard of such a thing as FitNesse . This is one of the testing technologies where tests are created as wiki markup (i.e., each test is a web page), and then run on a specific technology (Java, .Net, PowerShell, etc.).

In this article I will talk about an example Using FitNesse to test .Net applications. And at the same time I will show you some tricks and tricks to reduce your development time. By the way, all these technologies are absolutely free.

What does it look like

Like I said, all tests in FitNesee look like web pages. The entire test can be represented as a sequence of tables, with each table containing one function. But this is a theory, but in practice, a function call looks like this:

login asSome user


I’ll explain right away that the Khabrov tablets look stretched. In fact, FitNesse will compress them to a minimum size - see here: http://fitnesse.org/FitNesse.UserGuide.TwoMinuteExample .

This plate contains a call to the "loginAs" method, which receives "Some User" input. That is, the programmer implements the loginAs function, and then the tester can use it. As you already understood, the name of the function is the combined bold text. Parameters are plain text. Each cell contains only one parameter. Each cell also contains either bold text or plain text.

login history of userSome useris
WhenIP
07/05/2014192.168.0.1
07/06/2014192.168.0.1


In this case, the LoginHistoryOfUserIs function is already launched with the Some User argument. The output is expected to be a collection of objects, each of which has the When and IP properties.

In exactly the same way, you can apply a set of objects to the input (for example, settings - this plate with key / value pairs).

In total, each test is a sequence of similar tables. The programmer creates functions, the tester creates a test script with their help. Naturally, common pieces of code can be separated into separate files, templates are supported, you can define blocks that will run before each test, etc. The basic functionality is rich enough to test applications (at least business logic, separate from the UI )

How to run

As I said above, FitNesse can run functions on different technologies and programming languages. It looks like this:

  1. FitNesse parses the wiki markup, determines what needs to be launched (which web pages)
  2. FitNesse launches the selected Runner in the required programming language and tells it what and how to run. Moreover, only text is transmitted, there are no objects here
  3. Runner already knows what classes it works with. It parses the plate and calls the function


In this article I will describe the work on the .Net platform, so all classes and functions will be from this world. In order to determine Runner, you should write, firstly, these lines

:! Define COMMAND_PATTERN {% m% p}
! Define TEST_RUNNER {.. \ binary \ currentBuild \ NetRunner.Executable.exe}
! Path .. \ binary \ testBuild \ NetRunner.InternalTests.dll

The first refers to the Runner ( this Runner is used in the example ). It determines how FitNesse will run Runner. All these arguments + system variables are passed to the input, but these are already details.
The second line is the path to Runner.
The third line is the path to the dlls (there may be several!) That contain your API. Everything is simple.

And here is an example foranother Runner'a :

! the define TEST_RUNNER {$ {} working_directory Lib \ fitsharp \} Runner.exe
the define COMMAND_PATTERN {% m -a $ {working_directory} \ MyTests.dll.config -r fitnesse.fitserver.FitServer, $ {working_directory! } Lib \ fitsharp \ fit.dll -v% p}
! Path $ {working_directory} MyTests.dll

As you can see, here I already defined the $ {working_directory} variable initially. And further, depending on its values, Runners from different folders will be launched and, most importantly, the tests are also in different folders. Such a structure is very useful on a build platform, where often there can be several branches for which builds and tests work.

So, to make this whole system work:
1. Download and run FitNesse
2. Download one of the Runners
3. Create your own library, which references Runner’s libraries and implements a minimal API
4. Create a test that calls the functions of your library
5. Run it

Everything, after that you already have a little test that does at least something. Then it’s the small business - to improve the quality of the API, increase the number of tests and run them during the build process.

How to create a test API?

As you see above, creating tests using the API is extremely simple: you just call functions, compare values, create a business script. Naturally, for this it is necessary to create high-quality functions for launching, so that the "emulation" of business actions takes place. Indeed, in fact, the integration test is an ordinary tester that works 24/7 and can click the mouse very quickly.

But the creation of a test API has its own difficulties. In my examples, I will use this Runner , since it is the most common (there is a mention of it even on the FitNesse website).

First you need to understand how this fitSharp works. And he does the following things:
  1. Defines a list of dlls to run
  2. Defines the configuration file to use.
  3. Creates a new Application Domain for tests
  4. Sequentially launches the functions requested by FitNesse


You also need to create classes that contain these same functions to run. For this:
  1. Create a project
  2. Add This NuGet Package
  3. Create a class that inherits DoFixture. Name the class, for example, MyTestFixture
  4. Create some method like public void MyTest ()
  5. Open FitNesse (I hope you already have it copied and running)
  6. Create a new test in it
  7. Define TEST_RUNNER, COMMAND_PATTERN, and! Path roughly as I described above. Use absolute paths best, so it’s easier to get started.
  8. Add a table with just one cell - [namespace] .MyTestFixture. Wiki text will look like | ''! - [namespace of class MyTestFixture] .MyTestFixture-! '' |. This is essentially the full name of the class.
  9. Add a table with one cell too - MyTest: | '' 'My Test' '' | (by the way, case is not important)
  10. Run the test


If there were no flaws, the test will start. So what have we done:

  1. The magic of creating classes. The thing is, initially fitSharp only finds classes that inherit from DoFixture. Then he will create them and run functions. Classes can be internal.
  2. An explanation to FitNesse what and where we will be launching.
  3. Magic with Fixture by default. The fact is that fitSharp requires a style function | class name | function name + arguments |. Most often, all functions are stacked in one class. In order not to write it at the beginning each time, you can define it once from above. Unfortunately, redefinition is not always possible (there is an opinion that this is a bug). And a few too can not be determined.
  4. Run test function. With this, everything is very simple: what function was, they launched such a function.


With simple texts, more or less clear. Let's move on to the difficult ones.

Complex parameters

Consider the LoginAs function (string userLogin). More or less clear what it should do in terms of meaning - it should be part of a certain system, and then the test environment should work as if from this user. Everything is logical. However, what parameter should be passed to the input? Option 1 - just pass the line, and then disassemble it. It’s not very convenient, because if we have two functions: login and Get Login History, then we will have to repeat the parsing procedure, etc.
Although this is not documented, in reality there is a very simple solution: if you want to accept a class as an input, then it must have a public static method Parse (string arg). Which, naturally, will return our class. For example, Int32 fits the definition completely (for structures this also works). Using this scheme, we get a good division of responsibility: our test methods will work with already more or less processed data. And all exceptions during parsing will still be displayed to the user.
An absolute analogy occurs with methods that return collections (see above). They are checked using a plate, that is, the tester provides the expected data as input, and your function should return the correct collection. In this case, if you return a class in which all fields can be parsed (well, that is, all types have the Parse method), then not a string comparison, but a comparison of objects. Which is already completely controlled by you.

Entry plate

It is often convenient to add data to the system using tables. A standard example is any Mapping (for example, the settings are Map: name -> value). That is, in fact, a table and several columns are transferred to the input. This can also be done in fitSharp. To do this, you need to create such a method:
public SetUpFixture SetSettingsForUserAs (User user). The method name and arguments can be any, but the return value is exactly SetUpFixture.
Next, we implement the class -

internal sealed class MySettings : SetUpFixture
{
     private readonly User user;    
     public MySettings(User user)
     {
           this.user = user;
     }
     public NameValue(string parameterName, string parameterValue)
     {
     }
}


And in the end, our table will look like this:

Set Settings For UserhabrauserAs
NameValue
First nameJohn
Last nameDoe


How the mapping of columns occurred is more or less clear. And the arguments are now served in the form of such labels.

Auto start

So, you have created a test. Or even a test suite - Test Suite. And they earned at FitNesse. Now is the time to start launching them automatically.
There are two main ways to start:
  1. Using the rest request, run a test, download xml (for example,
    http: // myServer: 8080 / MyTests? suite & format = xml
    ) The result will be an xml file with information on what was performed, how it worked, time, result, etc.
  2. Run another copy of FitNesse in run and close mode: java.exe -jar "fitnesse-standalone.jar" -d D: \ -c "MyTests? Suite & format = xml" -b "d: \ builds \ TestsResults.xml ". In this case, FitNesse will start, execute the rest request on itself and, after the answer, will close.


The second method always works, that is, running FitNesse is not required, moreover, you can keep FitNesse working and at the same time run tests in this way. All results will appear in both FitNesses.

Total


As a result, in this article I tried to quickly and quickly go over the tops and tell how to create and run FitNesse integration tests. Most often they go through these steps through trial and error, but if I had this article a year ago, it would have saved a couple of days for sure. I hope she helps you too.

Also popular now: