📜 ⬆️ ⬇️

Using FitNesse for .Net applications

Hi, Habr.

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

In this article I will tell you about the example of using FitNesse for testing .Net applications. And at the same time I will show a few tricks and tricks to reduce your development time. By the way, all these technologies are absolutely free.

What it looks like

As I said, all the 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 the function call looks like this:
')
login asSome user


Immediately I will explain that the Habrovsky plates look stretched. In fact, FitNesse will compress them to a minimum size - see here: http://fitnesse.org/FitNesse.UserGuide.TwoMinuteExample .

This label contains a call to the “loginAs” method, which receives “Some User” as 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. Also, each cell 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 launched with the argument Some User. At the exit, a collection of objects is expected, each of which has the When and IP properties.

Absolutely similarly, you can input a set of objects (for example, settings — this label with key / value pairs).

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 sufficiently rich to test applications (at least, business logic, separate from the UI )

How to run

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

  1. FitNesse parses wiki markup, determines what needs to be run (which web pages)
  2. FitNesse launches the selected Runner in the required programming language and tells it what to run. Moreover, only text is transmitted, there are no objects here.
  3. Runner already knows which classes it works with. He parses the table 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 the Runner, you should first write these lines:

! define COMMAND_PATTERN {% m% p}
! define TEST_RUNNER {.. \ binary \ currentBuild \ NetRunner.Executable.exe}
! path .. \ binary \ testBuild \ NetRunner.InternalTests.dll

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

And here is an example for another Runner :

! define TEST_RUNNER {$ {working_directory} Lib \ fitsharp \ Runner.exe}
! 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 have initially defined the variable $ {working_directory}. 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 the build platform, where there can often be several branches for which builds and tests run.

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

Everything, after that you already have a small test that does at least something. Then it’s a small matter 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 that use the API is extremely simple: you simply call functions, compare values, create a business script. Naturally, for this, it is necessary to create high-quality functions for launching, in order to “emulate” business actions. After all, in fact, the integration test is an ordinary tester who works 24/7 and can click a mouse very quickly.

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

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


You 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, for example, public void MyTest ()
  5. Open FitNesse (I hope it has already been copied and launched)
  6. Create a new test in it
  7. Define TEST_RUNNER, COMMAND_PATTERN, and! Path just about the way I described above. Use absolute paths best, so it will be easier to start.
  8. Add a table with just one cell - [namespace] .MyTestFixture. Wiki text will look like | ''! - [namespace of class MyTestFixture] .MyTestFixture-! '' |. In essence, this is the full class name.
  9. Add a table with a single cell too - MyTest: | '' 'My Test' '' | (by the way, the case of characters is not important)
  10. Run the test


If there were no flaws, then the test will start. So, what we did:

  1. Magic with the creation of classes. The fact is that initially fitSharp finds only classes that inherit from DoFixture. Further it will create them and launch functions. Classes can be internal.
  2. Explaining FitNesse'u, what and where we will run.
  3. Magic with fixture by default. The fact is that fitSharp requires a function in style | class name | function name + arguments |. Most often, all functions are combined in one class. In order not to write it at the beginning each time, you can define it once from above. Unfortunately, it is not always possible to redefine (there is an opinion that this is a bug). And a few also do not define.
  4. Run test function. With this, everything is quite simple: what function it was, this one was launched.


With simple texts more or less clear. We turn to the difficult.

Difficult parameters

Consider the LoginAs (string userLogin) function. It is more or less clear what it should do according to its meaning - it should be included in a certain system, and then the test environment should work as if from this user. Everything is logical. However, what parameter to pass to the input? Option 1 - just pass a line, and then disassemble it. Not very convenient, since if we have two functions: login and Get Login History, we will have to repeat the parsing procedure, etc.
Although it is not documented, in fact there is a very simple solution: if you want to take a class as input, then it should have a public static Parse method (string arg). Which, naturally, will return our class. For example, Int32 fully fits the definition (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 when parsing will still be displayed to the user.
An absolute analogy occurs with the methods that return collections (see above). They are checked with the help of the table, well, that is, the tester sends the expected data to the input, and your function must 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 it will not be a string comparison, but a comparison of objects. Which is already fully controlled by you.

Entrance sign

It is often convenient to add data to the system using tables. The standard example is any Mapping (for example, the settings are Map: name -> value). That is, in fact, a table and several columns are transmitted 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 name of the method and the arguments can be any, but the return value is 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 the columns occurred is more or less clear. And the arguments are now served in the form of such tablets.

Auto start

So, you have created a test. Or even a test suite - Test Suite. And they earned in FitNesse. Now it's time to start running them automatically.
There are two main ways to run:
  1. Using the rest request to perform a test, download xml (for example,
      http: // myServer: 8080 / MyTests? suite & format = xml 
    ). The result will be an xml file with information about what was performed, how it worked, time, result, etc.
  2. Run another copy of FitNesse in the "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 itself and, after the response, close.


The second method always works, that is, it does not require running FitNesse, moreover, you can keep FitNesse running and simultaneously perform tests in this way. All results will appear in both FitNesse.

Total


As a result, in this article I tried to quickly run over the tops and tell you how to create and run the FitNesse integration tests. Most often, these steps go through a trial and error method, 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.

Source: https://habr.com/ru/post/228967/


All Articles