Friday, February 16, 2024

Avocado-VT - Test the KVM virtualization stack on system level

 Our team uses Avocado-VT to implement Virtualization system tests from Libvirt or QEMU level.

Avocado-VT is highly-configurable and as such we've seen many people struggling getting started with it despite the documentation.

The following slides provide a hands-on step-by-step setup, execution and development overview that might help get you started. It focuses on an audience for testing on mainframes but most parts are arch-independent. If there's anything missing or wrong, please let me know.




Thursday, December 1, 2022

Host is down - one way to fix broken systemd

kill -15 1

Some of our automation jobs sometimes seems to break systemd on our hosts resulting in the following error when interacting with it (e.g. trying to reboot):

System has not been booted with systemd as init system (PID 1). Can't operate.

Failed to connect to bus: Host is down

Failed to talk to init daemon.

Clearly this is a lie as ps confirms.

"Host is down" solutions recommend usually to issue some systemctl command but that just caused the same behavior.

However, you can issue those commands by sending signals as the manpage reveals. In our case sending SIGTERM to PID 1 solved the issue (though not sure what causes the issue in the first place :/).

SIGNALS

       SIGTERM

           Upon receiving this signal the systemd system manager serializes its state, reexecutes itself and deserializes

           the saved state again. This is mostly equivalent to systemctl daemon-reexec.

Tuesday, July 19, 2022

Persistent crypto device passthrough on s390x KVM

Overview

With v2.22.0 the s390-tools (aka s390utils on RHEL) have an important addition that helps to pass through crypto domains to KVM guests: they allow for persistence and help the user to avoid invalid configurations. (Thanks to Matthew Rosato for explaining the details to me.)

As you might remember from my previous post, crypto device passthrough (with libvirt) consists of three main steps:

  1. Remove the host driver from the device and assign vfio_ap
  2. Start a mediated device of type vfio_ap-passthrough with assigned adapter, usage domain and optionally control domain
  3. Attach the mediated device via its UUID and the <hostdev> element to the KVM domain
For a while now, we have the nice tool mdevctl to help with step 2. above, see for example RHEL 8 official user documentation. Actually, libvirt also integrates with mdevctl, so you can manage your mediated devices via libvirt's nodedev API.

However, if you reboot your LPAR, several configurations need to be persisted in your environment:

  1. Of course, you need the mediated device configuration and the KVM definition to be persisted.
  2. The passthrough driver needs to be loaded; this might depend on the kernel you are using. On RHEL you can configure the kernel module vfio_ap to be automatically loaded at boot as described here. Otherwise, trying to define a device via the nodedev API might just tell you: unsupported configuration: invalid parent device 'ap_matrix'
  3. Finally, the crypto devices' driver assignments need to be persisted.
While 1. is taken care of the libvirt (with mdevctl's help), 2. and 3. are what the mentioned s390-tools provide with release v2.22.0. (Mind that it leverages kernel uevents BINDINGS=complete and COMPLETECOUNT=X that might not be available in older kernel versions, e.g. v4.x.)

As a result, we now can setup workloads in KVM machines that leverage the crypto devices (e.g. hardware accelerated crypto operations), set it up once, set it to start automatically and after we reboot the LPAR (e.g. when we apply important security updates to the kernel), our KVM are restarted automatically.

How to persist crypto device driver assignments

Suppose the vfio_ap kernel module is loaded (v2.22.0's new ap udev rule takes care of this, too) and the mediated device configured as well.
In order to configure the device driver assignment, instead of using sysfs directly, we'll make use of the new type ap on the lszdev and chzdev commands.

Check if your s390-tools version supports ap handling

Simply run

# lszdev --list-types

and confirm that it's listed

...
ap           Cryptographic Adjunct Processor (AP) device
...

Configure apmask and aqmask with chzdev

chzdev is not a new tool. Important for the persistence of device configurations are the flags:

       -a
       --active
           Apply changes to the active configuration only
...

       -p
       --persistent
           Apply changes to persistent configuration only.

where omitting both flags has the same effect as passing them both: any change is applied immediately and will be restored on reboot.

The interesting part is how the apmask and aqmask are configured: With this tool we use a decimal base which can be more convenient. You might remember that each mask represents the adapters resp. domains through an array of 256 bits, where a 0 says 'used by the host' and a 1 'free for use by other driver (vfio_ap)', details see kernel doc.

We've been used to the hexadecimal representation when using the lszcrypt tool or the sysfs directly, e.g.

# lszcrypt

CARD.DOM TYPE  MODE        STATUS     REQUESTS
----------------------------------------------
02       CEX7A Accelerator online            0
02.002b  CEX7A Accelerator online            0
02.0032  CEX7A Accelerator online            0
02.0033  CEX7A Accelerator online            0

So, if we want to pass the domain 02.002b through we have to get their decimal values. For those who are not good at base change and have a bash console you can achieve this - e.g. for 2b - like this:

# echo "obase=10; ibase=16; 2B" | bc
43

(It's important you use the upper case letter.)

Now instead of echoing into the sysfs path, you can simply issue

# chzdev -t ap apmask=-2 aqmask=-43

(remember the "-" means to "take away" from the host)

and confirm via

# lszdev -t ap
DEVICE TYPE ap
  Description        : Cryptographic Adjunct Processor (AP) device
  Modules            : ap
  Active             : yes
  Persistent         : yes

  ATTRIBUTE  ACTIVE         PERSISTENT
  apmask     "0-1,3-255"    "0-1,3-255"
  aqmask     "0-42,44-255"  "0-42,44-255"


The tool will write out the persistent setting in a udev rule making sure the assignment is restored after reboot.

chzdev allows for more sophisticated operations, e.g.

# chzdev -t ap apmask=+2,-4-8 aqmask=+43,-0-42

will return 02.002b to the host and give the ranges 4-8 and 0-42 to the vfio_ap driver

# lszdev -t ap
DEVICE TYPE ap
  Description        : Cryptographic Adjunct Processor (AP) device
  Modules            : ap
  Active             : yes
  Persistent         : yes

  ATTRIBUTE  ACTIVE       PERSISTENT
  apmask     "0-3,9-255"  "0-3,9-255"
  aqmask     "43-255"     "43-255"

This also allows us to easily reset all of our configurations during testing via chzdev -t ap apmask=0-255 aqmask=0-255.

Protection against bad configurations

As a further improvement for users, the tools check the current environment and integrate with mdevctl to help avoid conflicting configurations. For example:

Don't allow for mediated device definitions if another device already uses the same device

With a the following nodedev successfully defined, we can't return the adapter to the host:

# virsh nodedev-dumpxml 
<device>
  <name>mdev_d36d7d0f_cf3d_4fef_bb9c_ed393954996b_matrix</name>
  <path>/sys/devices/vfio_ap/matrix/d36d7d0f-cf3d-4fef-bb9c-ed393954996b</path>
  <parent>ap_matrix</parent>
  <driver>
    <name>vfio_ap_mdev</name>
  </driver>
  <capability type='mdev'>
    <type id='vfio_ap-passthrough'/>
    <uuid>d36d7d0f-cf3d-4fef-bb9c-ed393954996b</uuid>
    <parent_addr>matrix</parent_addr>
    <iommuGroup number='1'/>
    <attr name='assign_adapter' value='0x02'/>
    <attr name='assign_domain' value='0x002b'/>
  </capability>
</device>

# chzdev -t ap apmask=+2
chzdev: apmask conflicts with mdev d36d7d0f-cf3d-4fef-bb9c-ed393954996b APQN 2.43
chzdev: persistent apmask conflicts with defined autostart mdev d36d7d0f-cf3d-4fef-bb9c-ed393954996b APQN 2.43
ap device type configure failed
    Error: Invalid configuration


If interested you can check out further validations in ap_check's source code here.

Thursday, February 3, 2022

Customer focus powered by SBT and Automation

Recently we experimented with Session Based Testing (as opposed to scripted testing) and reflected

about how to integrate it in our processes.

We asked questions like

  • What makes human testers unique?
  • Is there space for product validation in regression testing?
  • Can we reduce test documentation efforts?

  • This was a collaborative effort. I am very grateful for everybody who gave feedback, joined testing sessions or participated in some other way to help us learn and improve our Testing.

    Hopefully, others will join the conversation and maybe find something useful in our findings.

    You can download the slides with speaker notes as PDF here.

    Thursday, July 8, 2021

    Testing shared memory communications with Linux on Z

    Mainframes allow for shared memory communications between LPARs on the same box through ISM - internal shared memory. More information about the Linux device driver can be found here.

    IBM provides open-source tools including smc_run which easily converts an application's usage of TCP/IP sockets to SMC (shared memory connection) sockets.

    Let's suppose you want to check two of your LPARs can communicate with each other.

    First you'd want to check if ISM is available. As ISM are made available as virtual pci devices, you can simply run lspci on each LPAR.

    # lspci
    00:00.0 Non-VGA unclassified device: IBM Internal Shared Memory (ISM) virtual PCI device
    If your admin tells you have been provided the ISM device but you don't see it you might have to power it on.
    # echo 1 > /sys/bus/pci/slots/0000032/power
    Now, smc_run will convert any TCP/IP socket usage to an SMC socket. So let's suppose you have an echo server using the AF_NET protocol that you can start via commandline; you'd simply run the same command.
    [host1]# smc_run python3 echo_server.py --host my_host_name.example.org --port 12345
    You can then simply send data from a client in the same way.
    [host2]# smc_run python3 send_data.py --host my_host_name.example.org --port 12345
    some data
    Looks like it's working right? But does it really?

    If you power off one of the ISM devices, the above scenario still works. How can that be?

    Reading through the manpages, we'll find in the af_smc manpage:

    SMC socket capabilities are negotiated at connection setup. If one peer is not SMC capable, further socket processing falls back to TCP usage automatically.

    So, how can we make sure that our LPARs really communicate through the ISM?

    The s390-tools luckily deliver another tool smcss. It shows details for AF_SMC socket connections. The Mode column shows how data is exchanged:

    SMCD     The SMC socket uses SMC-D for data exchange.

    SMCR     The SMC socket uses SMC-R for data exchange.

    TCP        The SMC socket uses the TCP protocol for data exchange, because an SMC connection could not be established.

    And really, the difference can be confirmed while the connection is open depending on the availability of ISM on both LPARs.

    [host1]# smcss
    State   UID   Inode   Local Address       Peer Address        Intf Mode
    ACTIVE  00000 22045079 192.168.0.10:12223  192.168.0.12:37060  0000 SMCD
    
    vs.
    [host1]# smcss
    State   UID   Inode   Local Address       Peer Address        Intf Mode
    ACTIVE  00000 22049662 192.168.0.10:12223 192.168.0.12:37058  0000 TCP 0x05000000/0x03030000
    

    Finally, the s390-tools since version 1.5 also offers another tool that helps to check the ISM live-connectivity without a TCP application, smc_chk. You can shortly run:

    [host1]# smc_chk -S
    Server started on port 37374
    [host2]# smc_chk -C 192.168.0.12 -p 37374
    Test with target IP 192.168.0.12 and port 37374
      Live test (SMC-D and SMC-R, EXPERIMENTAL)
         Success, using SMC-D

    Friday, September 4, 2020

    How to align on the right - partition alignment algorithm

     In MSDOS 6.22 there are alignment restrictions for partitions. This means a partition of size capacity = /start - end/ = end - start, partition boundaries (start, end) must coincide with certain boundaries; in this case cylinder boundaries.



    The following alignment algorithm is taken from the libvirt virtualization API.

    In short, the algorithm will make sure allocated continuous space is aligned on the right, that is on the end. If the available free space for alignment already starts at a given boundary value, it will be fully aligned [1].

    We'll have:

    1. Input: c := capacity, l := alignment interval, s := start
    2. Output: e := end
    All of these values are in ZZ (actually NN). For e: c <= e - s; e + 1 = n*l (for some natural n), that is, the required capacity fits into the allocated space and the end is aligned while it must end one unit before the next interval starts.

    Let r := l - (c mod l). We understand as the extra space required to reach the interval boundary, e.g. if l is 512 (think of sector size) and I need to allocate capacity 618, then 1*l won't cover c, instead I'd have to used 2*l = 1024 >= 618. But then I have 1024 - 618 = 406 = 512 - (618 mod 512) of extra space I need to allocate that wasn't really required.

    The algorithm handles three cases:
    1. s = m*l, for some m (the start is aligned at a boundary)
    2. s != m*l; s mod l <= r (the start offset fits into the extra space reserved for alignment)
    3. s != m*l; s mod l > r (the start offset doesn't fit) 
    For 1. the correct e is quite easy, we already know how much extra space to align and subtract 1 to have the partition end just before the next boundary in order to have the next partition start exactly at boundary.

    (1)    e = s + c + r - 1

    This is the base for the other two cases.

    For 2. (1) would surpass the boundary:

    boundary=s       ...         s+c          boundary=s+c+r
             |                ...           |                        |

    boundary          s         ...       s+c   boundary       s+c+r
             |               |                      |                |              |

    But we know that s mod l <= r, therefore s+c+r - s mod l >= s + c proving that the alignment on the right would fit the required capacity. Thus:

    (2)    e = s + c + r - s mod l - 1

    And e is still on boundary: e = s + c + r - s mod l - 1 =  (c + r) + (s - s mod l) - 1 = n_1*l + n_2*l - 1.

    Now for 3. from the above,   e - s = s + c + r - s mod l - s = c + r - s mod l < c. If we originally had defined r to be 2*l - (c mod l), then e - s > c. But we didn't do that because for 1. and 2. that would be a waste of space. However, here for 3. we don't have another choice, so we add another l:

    (3)    e = s + c + r + l - s mod l - 1

    In the referenced algorithm, you'll see that s mod l is always subtracted. Let's keep in mind that s is at a boundary iff s mod l = 0. So we can actually summarize

                     
    (4)    e = s + c + r + d - s mod l - 1, where d := 0 if s mod l < r, else d := l.
    QED.

    [1]  I wonder if the need for a first partition not starting exactly at the second cylinder to save space or some other MSDOS restrictions are the reason for not aligning the partition start, too.

    Monday, July 27, 2020

    Set up Crypto Card passthrough with KVM on IBM Z (vfio-ap)

    Crypto Cards on IBM Z systems provide secure key encryption.

    This security feature can be passed through to KVM guests.

    The passthrough is available through the vfio_ap kernel module (paired with the homonymous driver). It uses another passthrough interface, namely, the VFIO mediated device framework (represented by kernel module vfio_mdev).

    More details can be found kernel doc. Here, the focus is on setting up a single passthrough using libvirt.

    What we need
    1. A System Z host with a crypto card, KVM guest
    2. lszcrypt command (from s390tools, often comes preinstalled with distro, package name can be s390utils, too)
    What we do
    1. Identify the device
    2. Mark device queues as not usable by host
    3. Create mediated device
    4. Assign crypto device to mediated device
    5. Attach mediated device to guest
    6. Verify setup
    Identify the device

    # lszcrypt -V

    CARD.DOMAIN TYPE  MODE        STATUS  REQUESTS  PENDING HWTYPE QDEPTH FUNCTIONS  DRIVER     
    --------------------------------------------------------------------------------------------
    01          CEX5C CCA-Coproc  online         1        0     11     08 S--D--N--  cex4card   
    01.0011     CEX5C CCA-Coproc  online         1        0     11     08 S--D--N--  cex4queue  

    We need two pieces of information:
    • HWTYPE: passthrough is only supported if this number is >= 10
    • CARD.DOMAIN: 0x01 (adapter id), 0x0011 (domain id)
    Mark device queues as not usable by host
    • Mark adapter not usable by host:
      • echo -0x01 > /sys/bus/ap/apmask
    • Mark device queues not usable by host:
      • echo -0x0011 > /sys/bus/ap/aqmask
    lszcrypt now should only list the card, not the queue.

    # lszcrypt
    CARD.DOMAIN TYPE  MODE        STATUS  REQUESTS
    ----------------------------------------------
    01          CEX5C CCA-Coproc  online         4



    Create mediated device

    cd /sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough
    uuidgen > create

    Assign crypto device to mediated device

    Below the device dir $uuid that we just created below vfio_ap-passthrough/devices:

    cd devices/$uuidgen
    echo 0x01 > assign_adapter
    echo 0x0011 > assign_domain

    We can confirm assignment:

    # lszcrypt -V
    CARD.DOMAIN TYPE  MODE        STATUS  REQUESTS  PENDING HWTYPE QDEPTH FUNCTIONS  DRIVER     
    --------------------------------------------------------------------------------------------
    01          CEX5C CCA-Coproc  online         4        0     11     08 S--D--N--  cex4card   
    01.0011     CEX5C CCA-Coproc  -              4        0     11     08 S--D--N--  vfio_ap    

    Attach the mediated device to guest

    Use $uuid for the mediated device and modify guest domain xml (e.g. virsh edit)
    <hostdev mode='subsystem' type='mdev' model='vfio-ap'>
        <source>
          <address uuid='$uuid'/>
        </source>
    </hostdev>


    Verify setup

    After starting the guest:

    root@guest # lszcrypt -V

    CARD.DOMAIN TYPE  MODE        STATUS  REQUESTS  PENDING HWTYPE QDEPTH FUNCTIONS  DRIVER     
    --------------------------------------------------------------------------------------------
    01          CEX5C CCA-Coproc  online         1        0     11     08 S--D--N--  cex4card   
    01.0011     CEX5C CCA-Coproc  online         1        0     11     08 S--D--N--  cex4queue  

    Tuesday, April 28, 2020

    The testing effort growth

    Just wondering how a proof for "Number of test cases grows exponentially" could look like...

    Define a program to be a function from a set of input variables to a set of output variables,

    P = Input x P|Output = I x O 
      = { (j_1,...,j_n) x (o_1,...,o_m) }
      = { (j_1,...,j_n,o_1,...,o_m) }.

    Each component of Input is supposed to have at least 2 elements. (If not, the input variable will never change the image value, in other words, the program's behavior, and can therefore be eliminated. The case where an input value is determine to be defined or not, Input ≃ { 0, {0} }.)

    Also, I need to restrict to the case where the dim(I) > 1 because if not each extra value to test adds exactly one test case (linear growth).

    Define a feature to be a selection of a subset of input variables combined with their image, that is, it's a restriction of the program to a subset of the domain, F = P|J, J < I.

    We take a test case to be the a random variable

    T : Input x Î© -> Input x Output
    T(i, ω) = (i, T_1(i, ω),..., T_m(i, ω)).

    Each T_j is an expected output or simply expectation or post-condition.

    A test passes if T_j(ω) = P(i)_j for all j, or fails otherwise, for a test execution Ï‰.

    A test case of a feature is then simply T|F = T|J x Ω where F = P|J.

    For a given feature we can add new behavior minimally by
    1. extending the domain of an input variable J_i by an additional value j' that defines a new behavior of the program, that is for some i, J_i' = J_i + { j' } and Input = J_1 x ... x J_i' x ... x J_n
    2. extending the set of input variables by an additional dimension J_n+1
    Both will result in an extended feature F'.

    For 2. it is easy to see that

    #T|F' = #{ (J_1,...,J_n) x J_n+1 x O } 
          = #J * #J_n+1 * # O 
          = #T|F * #J_n+1
          ≥ #T|F * 2.

    As for 1., given that dim(J) > 1 each new value j' creates another full set of combinations of input variables (j_1,...,j',...,j_n), that is, the number of added test cases is

    #T|F' - #T|F = #J_1,...,^J_i,...,J_n
                                   ≥ 2^(n-1)

    where ^J_i denotes not selecting this component.

    Therefore,

    #T|F'  #T|F + 2^(n-1).

    So, in general adding a new value to test, the lower bound for growth is 2^(n-1).  ⃞

    Considering that the effort E|F of testing a feature depends on the selected test suite S|F < T|F, we might dare say that selecting S|F and the methods to evaluate T(i, ω) is a very important activity in testing.

    I hope this makes sense...

    Thursday, March 26, 2020

    ssh into libvirt guest

    Libvirt sets up NAT mode per default allowing guests to communicate, for example, with the internet, with each other and with the host. A host interface vnetX will be created and normally an IP will be assigned automatically.


    # virsh domifaddr vm
     Name       MAC address          Protocol     Address
    -------------------------------------------------------------------------------
     vnet0      52:54:00:6f:dc:90    ipv4         192.168.122.84/24
    
    # virsh dumpxml --inactive vm
    ...
        <interface type="network">
          <mac address="52:54:00:6f:dc:90">
          <source network="default"></source>
          <model type="virtio">
          <address bus="0x00" domain="0x0000" function="0x0" slot="0x03" type="pci">
        </address>
    ...
    

    However, vnetX won't get the IP assigned really, that's not how it's intended. So, per default neither the ip nor any name are available to ssh

    What you can do instead is use the NSS module libvirt-nss.

    If you want to easily ssh into the guest using its libvirt name, set
    # /etc/nsswitch.conf:
    hosts:       files libvirt_guest dns
    
    and make sure to have the correct sshd configuration in your guest.

    Tuesday, February 21, 2017

    Sunk Cost Fallacy - Let's talk about bias 3

    We continue our review of  biases we all have intrinsecally implemented in our own brains that lead us to make certain decisions, as Rolf Dobelli describes in his bestseller The Art of Thinking Clearly.

    Sunk Cost Fallacy - Are you Lean enough?

    This really is a very useful bias. I believe if we're honest enough with ourselves it helps us to really do a better job saving unnecessary costs for our company.

    Without much introduction, two cases that immediately come to my mind:

    1. You see this huge beautiful implementation arriving testing. Verification passes without any flaw. You then notice: validation fails! This is not really what we wanted. Do you dare to create a bug and vote for throwing away / reworking all that hard and good work? Do you try to bend in and persuade any opposing mind to believe that that's what they wanted? Sometimes this is not the worst choice - sometimes clients just don't know what they want XD
    2. You've convinced everybody you need those automated tests / manual test scripts / test management system - whatever - to improve your testing process and all over quality of your product(s). It becomes a nuissance in time, things change, tempora mutantur. Are you willing to let go, to not hold it back anymore? Throw away what lacks purpose and be really lean.

    Wednesday, January 11, 2017

    Social Proof - Let's talk about bias 2

    We continue our review of  biases we all have intrinsecally implemented in our own brains that lead us to make certain decisions, as Rolf Dobelli describes in his bestseller The Art of Thinking Clearly.

    Social Proof

    SCRUM planning session. A tester (role) points out that a certain US should include a some test to minimize risk of failure (functional, non-functional, however). The team and the product owner discuss the risk and convince themselves that the test is not necessary. The tester role has given in and will rest in peace.

    The social proof makes you believe a decision is right because everybody else seems to believe it's right.
    As tester you then have to ask yourself critically: is it their arguments that convince me or is it them being so convinced that does? If it's their arguments you might rest in peace, really. If not, argument against it or forever remain silent.

    Survivorship bias - Let's talk about bias 1

    You'll probably remember that a tester's profile has one of its main reasons for existence in avoiding author bias.

    As QA and testers dive into Agile environments it's hard not to become part of the problem.

    Interesting enough, there are well known biases we all have intrinsecally implemented in our own brains that lead us to make certain decisions, as Rolf Dobelli describes in his bestseller The Art of Thinking Clearly.

    This post starts a series of checks of those biases with QA specific examples, so any tester or QA role can improve their work. Besides Agile, I like the move forward Quality Assistance because I experience that it works. So anybody in the team should have some advantage by knowing these biases.

    Survivorship Bias

    You as QA are confronted with developers and business representatives telling you "product X didn't have all of those design patterns, automated tests, branching policies, etc."
    They strike you hard. You feel like earth's gonna swallow you. You're in China and only speak Hopi. Shit!

    This is not your bias! Let'em check out their bug reports and time (and money - YEEEES, business speaks MO-O-NEY!) spent on fixing them. Speak to them about your customers serving as testers. But we're not facebook, we sell other stuff under different conditions. And if you really can't find none of those arguments to have effect present them the worst case, find examples for companies who failed with much embarrassment or quit your job, right now there are great developers at work!


    Saturday, November 19, 2016

    Fast Frontend Development and HUI Testing with mockyeah

    The backend mocking library mockyeah has recently been added new features inspired by WireMock. The library has the big advantage of allowing you to re-use mock data during development for testing.
    By mocking the backend there's no immediate need to set up and maintain backend data thus speeding up development.
    You can find working sample code here.

    Fast frontend development - backend mocking

    In order to be independent from a real backend instance during frontend development we'll organize our expected responses and add them to a default setup. We'll then configure our frontend to point to the mockyeah instance, start the mock server with the default setup and we're ready to develop our web application!

    Organizing the mock data

    Organizing our mock data we'll have later easy access in your test definitions:
    // mock-data.js
    var usersGet = {
        pattern: /users$/,
        ok: {
            status: 200,
            json: [
                {
                    id: 1,
                    username: 'user1',
                    email: 'user1@email.com'
                },
                {
                    id: 2,
                    username: 'user2',
                    email: 'user2@email.com'
                }
                ,
                {
                    id: 3,
                    username: 'user3',
                    email: 'user3@email.com'
                }
            ]
        },
        ko: {
            status: 500,
            json: {errorCode: "UnexpectedException"}
        }
    };

    Collecting mock data

    mockyeah has a record-and-play feature that allows it to run as a proxy and save data you want to mock. Have a look at the library's documentation to find out more.

    Define a default setup

    //default-setup.js
    
    var mockyeah = require('mockyeah');
    var mockdata = require('./mock_data');
    
    var init_mock = function () {
        mockyeah.get(mockdata.usersGet.pattern, mockdata.usersGet.ok);
        mockyeah.get(mockdata.userGet.pattern, mockdata.userGet.ok);
        mockyeah.post(mockdata.userPost.pattern, mockdata.userPost.ok);
    };
    
    init_mock();
    
    exports.init_mock = init_mock;
    exports.mockyeah = mockyeah;
    exports.mockdata = mockdata;
    Now we run node default-setup.js and have a backend available during development.

    Hermetic User Interface (HUI) testing

    Frontend by itself is a subsystem of your application. HUI tests strive to have more stable and maintainable tests by mocking out any dependency to a service. This means you would normally implement system level tests with Selenium WebDriver or Protractor.

    Starting mockyeah for testing

    As mockyeah is automatically started as configured in .mockyeah when required, your test suite must simply import your default setup as defined above.
    var setup = require('./../default-setup');
    
    describe('Handle users', function () {
        describe('Users view', function () {
            beforeAll(setup.init_mock);
            afterAll(setup.mockyeah.close);
    

    Changing mockyeah behaviour during test definition

    Simply set the response in the test definition itself. Notice that we re-use the mockyeah exported by the default setup.
    describe('Handle users', function () {
        describe('Users view', function () {
            // ...
            
            it('should load the users list', function () {
                return usersPage.navigateToUsersView().then(function () {
                    expect(usersPage.getUserList()).toContain({id: 1, username: 'user1'});
                    expect(usersPage.getMessage()).toEqual("SUCCESS");
                });
            });
    
            it('should show the error code if list cannot be loaded', function () {
                setup.mockyeah.get(setup.mockdata.usersGet.pattern, 
                setup.mockdata.usersGet.ko);
                return usersPage.navigateToUsersView().then(function () {
                    expect(usersPage.getMessage()).toEqual("UnexpectedException");
                });
            });
    
    

    Request verification and logging

    During test development we'll want to inspect the received requests (the "request journal") when something doesn't come out as expected. We can do this by setting
    {
        output: true,
        journal: true
    }
    giving the output
          ✓ should send the correct request
    [mockyeah][12:31:46][SERVE][MOUNT][GET] /users$/
    [mockyeah][12:31:46][REQUEST][JOURNAL] {
      "callCount": 1,
      "url": "/users",
      "fullUrl": "http://localhost:4001/users",
      "clientIp": "127.0.0.1",
      "method": "GET",
      "headers": {
        "host": "localhost:4001",
        "accept": "application/json",
        "connection": "close"
      },
      "query": {},
      "body": {}
    }
    [mockyeah][12:31:46][REQUEST][GET] /users (2ms)
    
    If our application code composes a complex request from several sources sometime we find it useful to verify the sent request:
    it('should send the correct request', function() {
        return usersPage.navigateToUsersView().then(function () {
            usersPage.enterNewUserDetails("user1", "user1@email.com");
            var expectation = setup.mockyeah.post(setup.mockdata.userPost.pattern, 
                                                  setup.mockdata.userPost.ok)
                .expect()
                .body({
                    username: 'user1',
                    email: 'user1@email.com'
                })
                .once();
            return usersPage.confirm().then(function () {
                expectation.verify();
            });
        });
    });
    
    The standard configuration of mockyeah will write a standard request log which is very helpful during test development.
    [21:59:59] I/local - Selenium standalone server started at http://192.168.0.155:48608/wd/hub
    Spec started
    [mockyeah][SERVE] Listening at http://127.0.0.1:4001
    [mockyeah][REQUEST][GET] /users (2ms)
    
      Handle users
    
        Users view
          ✓ should load the users list
    [mockyeah][REQUEST][GET] /users (1ms)
          ✓ should show the error code if list cannot be loaded
    [mockyeah][REQUEST][GET] /users (1ms)
    [mockyeah][REQUEST][POST] /users (1ms)
          ✓ should load user details when created successfully
    
    Executed 3 of 3 specs SUCCESS in 0.062 sec.
    [mockyeah][SERVE][EXIT] Goodbye.
    [22:00:00] I/local - Shutting down selenium standalone server.
    However, for test reporting you might prefer to switch logging of by setting
    { ...
      "output": false,
      "verbose": false
    }
    which will give the following less cluttered output.
    [21:59:59] I/local - Selenium standalone server started at http://192.168.0.155:48608/wd/hub
    Spec started
    
      Handle users
    
        Users view
          ✓ should load the users list
          ✓ should show the error code if list cannot be loaded
          ✓ should load user details when created successfully
    
    Executed 3 of 3 specs SUCCESS in 0.062 sec.
    [22:00:00] I/local - Shutting down selenium standalone server.

    Sunday, June 26, 2016

    WireMock for your Dependant-On Http Service

    Recently I needed to mock an http service our appplication depends on. A developer recommended me WireMock he uses at unit level.

    I target the system level. But - wow - WireMock running as standalone server gives me the same feature set thanks to it's great JSON API for configuration while running.

    Here I give you an overview and some advice about how to get started mocking an HTTP service at system level.

    Collection of the Mocked Data

    First thing you need to know is:
    • the url you call
    • the data returned by those calls.
    WireMock has a great record and playback feature. WireMock proxies any calls to the DOS and automatically creates files for the responses and mappings.

    Example: GetWeather

    We'll record responses for calling the global weather API of WebserviceX.NET.

    Run
    java -jar wiremock-1.58-standalone.jar --proxy-all="http://www.webservicex.net/" --record-mappings --verbose
    and make a sample call to the GetWeather method, for example:
    curl --header "Content-Type: text/xml;charset=UTF-8" --header "SOAPAction:her.asmxww.webserviceX.NET/GetWeather" --data @request.xml http://localhost:8080/globalweather.asmx
    
    (request.xml containing a valid request, obviously).

    This will create a file containing the response in __files and a sample mapping in mappings in your current working directory.

    If you open the mapping just created you'll see something like:
    
    {
      "request" : {
        "url" : "/globalweather.asmx",
        "method" : "POST",
        "bodyPatterns" : [ {
          "contains" : "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://www.webserviceX.NET\">\n   <soapenv:Header/>\n   <soapenv:Body>\n      <web:GetWeather>\n         <!--Optional:-->\n         <web:CityName>Madrid</web:CityName>\n         <!--Optional:-->\n         <web:CountryName>Spain</web:CountryName>\n      </web:GetWeather>\n   </soapenv:Body>\n</soapenv:Envelope>"
        } ]
      },
      "response" : {
        "status" : 200,
        "bodyFileName" : "body-globalweather.asmx-8VMpb.json",
        "headers" : {
          "Cache-Control" : "private, max-age=0",
          "Content-Type" : "text/xml; charset=utf-8",
          "Content-Encoding" : "gzip",
          "Vary" : "Accept-Encoding",
          "Server" : "Microsoft-IIS/7.0",
          "X-AspNet-Version" : "4.0.30319",
          "X-Powered-By" : "ASP.NET",
          "Date" : "Sun, 26 Jun 2016 12:49:57 GMT",
          "Content-Length" : "691"
        }
      }
    }
    
    Woohoo! your first configuration for the JSON API.

    You can easily check your configuration / mock data, by restarting WireMock, but this time in standalone mode
    
    java -jar wiremock-1.58-standalone.jar
    
    
    and rerunning the above curl command.

    Configuration of the Mock via JSON API

    While you can have a static mock configuration in the mappings and __files folders, the really cool stuff is changing the configuration during runtime / testing on the fly calling the JSON API. This way we can even control delay for performance testing.

    Let's suppose you're running WireMock on it's standard port at your localhost:8080 without any previous static configuration. You then create a new configuration by posting
    {
      "request" : {
        "url" : "/globalweather.asmx",
        "method" : "POST",
        "bodyPatterns" : [ {
          "contains" : "GetWeather"
        } ]
      },
      "response" : {
        "status" : 200,
        "body" : ...,
        "fixedDelayMilliseconds": 2000
      }
    }
    
    to http://localhost:8080/__admin/mappings/new causing this way the mock to respond after 2 seconds.

    WireMock has many well documented features any tester could whish for.

    JSON API Cheat Sheet

    Here a listing for easy reference during test development.

    MethodDescriptionNotes
    __admin/reset Removes all stub mappings and deletes the request log. Great for clearing mock behaviour before setting up anything
    __admin/mappings Lists all configured mappings Helps while developing your tests
    __admin/mappings/save Saves all mappings This way you can create a static default setup easily, and refresh it if your mappings change.
    __admin/mappings/new Creates a new mapping Create a new mapping in setup or during a test, switching for example from success to error. By default, WireMock will use the most recently added matching stub to satisfy the request. But several mappings can even be prioritised.
    __admin/mappings/reset Removes all non-static mappingsLike full reset, but preserving your default setup.
    __admin/settings
    __admin/socket_delay
    Set the global settings for your stubbing You can vary over delays for your performance testing.
    __admin/scenarios/reset Reset all states to START. Again, great for resetting your test environment if you use stateful behaviour.
    __admin/requests/count
    __admin/requests/find
    __admin/requests/reset
    Lets you manage and check the requests log Check requests during test development and verify DoS have been called.
    find with body { "urlPattern" : "/" } let's you inspect all recorded requests.
    You can find the full API reference here.


    Tuesday, April 19, 2016

    OpenCover and FitNesse/fitSharp


    OpenCover is a great tool for measuring .NET code coverage. In ATDD some tests are written and documented below system level.
    If you use FitNesse/fitSharp the code coverage cannot be determined by calling FitNesse on the console via java -jar fitnesse-standalon -c args.

    But... the test runner, Runner.exe, is implemented in .NET.

    It is called from the FitNesse server with arguments args1 = -r {assembly list} HOST PORT SOCKET.

    You can get a coverage report from OpenCover by defining a new test runner using OpenCover as a proxy. The FitNesse server will call the proxy with args1. The new test runner (definable in the wiki via the global variable TEST_RUNNER) will call OpenCover.Console.exe which then calls the original runner passing args1 on and returning its error code with the -returntargetcode argument.

    Monday, February 17, 2014

    Breaking Dependency on Third Party Executables - Examples in Go


    Often in distributed systems our products depend on third party applications. In this post I describe how we can mock these in order to improve coverage, testability and ease the setup of the test environment.

    Imagine you have an application that calls an executable. You could for example create an XML document from a database and feed it to a third party application to get a PostScript file. We implement the Decorator Pattern and use a suitable class or function in our programming environment to
    1. Call the executable with or without certain arguments (input interface)
    2. Intercept the standard output and error to check our results and return to the main application (output interface)
    In .NET we could use the Process class to accomplish this: we wrap the CLI into a Process object and redirect stderr and stdout for verification.

    On the unit test level we can mock the decorator itself. But if we have legacy code which isn't unit testable, or at the integration test level, it's useful to mock the third party executable itself. This will:
    1. speed up testing in case the third party application would take a while to finish,
    2. improve coverage: by taking control over the third party application we can provoke error states and need less test data setup to comply with the interface,
    3. make it easier to log which arguments the application is called with.
    I will call a mocked executable FakeExe.

    If our executable expects certain inputs we can control our FakeExe's behaviour based on these. We create an arguments encoder and a decoder. Here is a short example in Go of how we could control the exitcode for an executable expecting an XML file as input:
     package fakeexe  
     //...  
     func EncodeArgument(exitCode int) string {  
          return strconv.Itoa(exitCode) + ext  
     }  
     type fakeExe struct {  
          ExitCode int  
     }  
     func (f *fakeExe) DecodeArgument(arg string) {  
          var err error  
          if len(arg) > len(ext) {  
               ecode := arg[:len(arg)-len(ext)]  
               f.ExitCode, err = strconv.Atoi(ecode)  
          } else {  
               err = errors.New("input not valid" + arg)  
          }  
          if err != nil {  
               f.handleError(err)  
          }  
     }  
     func (f *fakeExe) Run(arg string) {  
          f.DecodeArgument(arg)  
     }  
     func (f *fakeExe) handleError(error) {   
     //...  
     }  
    
     package main  
     import (  
           "fakeexe"  
           "os"  
     )  
     func main() {  
           f := new(fakeexe.fakeExe)  
           f.Run(os.Args[0])  
           os.Exit(f.ExitCode)  
     }  
    

    The Encoder can be used in test setup code in order to generate the correct input args for the FakeExe. It can be extended with the following useful behaviour:

    1. Write a log: this way we can control that the Decorator calls the executable as expected and we can check the FakeExe in case the possible actions are more complicated than the easy example given above.
    2. Use input files from paths, for example if there is an input files directory which will be used as working directory. This can also be used to configure the FakeExe in case of several similar expected behaviours where we might not want to implement a seperate FakeExe for each executable we mock.


    At some point I faced the problem that the working directories of the executable wasn't known beforehand, it was created when the task using the Decorator was run. Furthermore, many instances of the same executable were called in a workflow. This created two problems:

    1. There was no use in writing the log per FakeExe instance: I needed a log of all instances together.
    2. Configuring the FakeExe by a configuration file beside it was undoable because it wouldn't have been copied to the target working directory.
    I solved the problem by implementing a logger and a configuration service which in Go reduced to just some lines of code, see http://golang.org/pkg/net/. The easiest implementation might look like this:

     package configuring  
     import (  
          "fmt"  
          "io/ioutil"  
          "net"  
     )  
     var ConfigFilename = "Config.txt"  
     //...  
     type server struct {  
          getListener func(protocol, port string) (net.Listener, error)  
          ln     net.Listener  
          protocol  string  
          port    string  
     }  
     func (srv *server) Start() {  
          var err error  
          srv.ln, err = srv.getListener(srv.protocol, ":"+srv.port)  
          if err != nil {  
               panic(err)  
     }  
          for {  
               conn, err := srv.ln.Accept()  
               if err != nil {  
                    fmt.Println(err)  
                    continue  
               }  
               go srv.sendConfig(conn)  
          }  
          return  
     }  
     func (srv *server) sendConfig(conn net.Conn) {  
          bytes, err := ioutil.ReadFile(ConfigFilename)  
          if err != nil {  
               panic(err)  
          }  
          if len(bytes) > MAX_MESSAGE_LENGTH {  
               panic("Config message too long.")  
          }  
          _, err = conn.Write(bytes)  
          if err != nil {  
               panic(err)  
          }  
          return  
     }  
     func (srv *server) Stop() {  
          if srv.ln != nil {  
               srv.ln.Close()  
          }  
     }  
    
     package logging  
     import (  
          "net"  
          "fmt"  
     )  
     //...  
     type server struct{  
          getListener func(protocol, port string) (net.Listener, error)  
          ln net.Listener  
          protocol string  
          port string  
          msgs []string  
     }  
     func (srv *server)Msgs() (msgs []string){  
          msgs = srv.msgs  
          return  
     }  
     func (srv *server)Start() (err error){  
          srv.ln, err = srv.getListener(srv.protocol, ":" + srv.port)  
          if(err != nil){  
               panic(err)  
          }  
          for {  
               conn, err := srv.ln.Accept()  
               if (err != nil){  
                    fmt.Println(err)  
                    continue  
               }  
               go srv.appendMessage(conn)  
          }  
          return  
     }  
     func (srv *server)appendMessage(conn net.Conn){  
          defer conn.Close()  
          buf := make([]byte, MAX_MESSAGE_LENGTH)  
          msg_length, err := conn.Read(buf)  
          var msg string  
          if(err != nil){  
               msg = err.Error()  
          }else{  
               msg = string(buf[:msg_length])  
          }  
          srv.msgs = append(srv.msgs, msg)  
     }  
     func (srv *server)Stop(){  
          if(srv.ln != nil){  
               srv.ln.Close()  
          }  
     }  
    

    From this the next step could be the implementation of a little DSL for our testing extending the FakeExe with an interpreter like for example in Bob's Blog - Writing a Lisp Interpreter in Go. Then, instead of sending concrete implementation specific configuration values with the configuration service we just send a script:

     package fakeexe  
     import (  
          "lisp" //https://github.com/bobappleyard/golisp  
          "io"  
          "strings"  
     )  
     //...  
     func (f *fakeExe)Run(script string){  
          i := lisp.New()  
          i.Repl(strings.NewReader(script), io.Stdout)  
          //...  
     }  
    

    *Examples are written in Go. 

    Wednesday, June 19, 2013

    Agile manual test case creation and management

    What happens if you work in an environment where you have to run acceptance tests manually; an environment where you cannot run an automated regression test suite in each iteration and thus can't report automatically which test cases have to be adapted or marked as obsolete?  Is agile even possible without automation?

    In this post I try to propose a workflow for agile testers - agile as in agile athlete - to conquer this essential lack of automation using TMS like TestLink  (finally surrendering myself to using the word "agile" in one of my posts). You might ask: "Why is he talking about regression and release test suite and at the same time about agile?" Let me answer this: agile methodologies, e.g. SCRUM, might not always be correctly implemented, and though this might lead to failure (sometimes), testers have to adapt to the environment they test in.

    Lifetime of an agile test case

    First there are acceptance tests, then these are extended in order to find bugs or pursue other test goals. So we are in an iteration and have a set of test cases but in agile these can have quite different lifetimes. When testing manually we ask ourselves: How are the odds that this test has to be run again, ever? If test cases were automated the cost for running that test case again would be nearly zero. But if checked manually the picture changes a lot. For example:
    1. Imagine you develop a CMS. We will want to export the content, like a picture. There are tests for that. But in a later iteration that file will automatically be opened in MSPaint. This additional feature makes the first test set obsolete for manual testing: if the picture isn't opened in MSPaint, we'll check why that is and we'll find or not that the file has been exported before.
    2. If there was a dead button in the GUI, the fix should form part of our regression test suite. The lifetime of this test case is undetermined, but for now we know that it will survive until the next regression test.
    3. If we change test data or namings in the project, e.g. image vs. picture, the written test case will need refactoring in order to survive.
    The important point is that I suppose that testers have developed an intuition about the lifetime of the test cases. Testers assign life expectancies to test cases.

    Test cases with low life expectancy (throw-away test cases)

    If our intuition tells us that a test case is quite specific and won't probably need to be repeated ever again, it's enough if we write it down quickly and concretely, e.g.

    1.) Select a picture.
    2.) Click the Button labelled "Download".
    3.) Open the documents folder of the user.
    => There is a folder OurCMS. It contains the selected picture.

    These test cases can be flagged in the TMS. In TestLink we can assign Test Importance = Low or create a custom field "throw-away". And after the test case ran successfully we Deactivate this version. This way we know, we won't have to care about it again but still we document what has been tested.

    Test cases with high life expectancy

    If our intuition tells us that a test case describes a general or essential aspect of the functionality of the system, it will have a somewhat higher life expectancy. In our example 1.) above, in the beginning we might be talking about downloading and managing photos; later about graphics or images. Or we might change the default check out location. If we anticipate this change, we can reduce refactoring in future relase or regression tests by writing our test case as mimicking coded tests:

    Preconditions:
    var GRAPHIC = a picture
    var BUTTON = Download
    var CHECKOUT_FOLDER = $USER$/Documents/OurCMS

    Steps:
    1.) Select GRAPHIC
    2.) Click BUTTON.
    => CHECKOUT_FOLDER contains GRAPHIC_FILE.

    Testers can document decisions they took while testing by specifying the chosen test data easily:

    Notes:
    GRAPHIC = VacationsExamples/DayOnTheBeach.png

    As this test case is probably run again in the future we'll assign a higher prio and avoid deactivating it. Written the test case in this fashion, we'll be able to quickly refactor it when the system has changed by just adapting Preconditions.

    Of course, now we talk to humans as if they were computers, but surely this is how non-testers treat us, too, demanding manual testing and high coverage, maintainability and traceability.


    Thursday, May 30, 2013

    Some advanced code testing

    For those who don't want or don't have the time to read thick books or framework testing APIs, I have collected a few topics that can help improve your unit testing but might not always be obvious. Note that they're not purely restricted to C# even though the examples are.

    Mighty testing frameworks

    There are some mighty mocking and unit testing frameworks out there with impressing features like mocking dependent-on static methods, testing private members etc. Although in some cases these are vital, e.g. when writing tests for legacy code, they might lead you to write your code with lower quality taking less care about class design, dependencies etc. What's better: code quality or a new (and possibly costly) dependency on a mighty framework? Code quality does not depend on the language you're programming in, it depends mainly on you as a developer!

    Use delegates for dependency to static methods

    C# delegates can work like functions in languages like Python or Go where they are first class citizens. This is a good thing, e.g. in the following case:
     public void Init()  
     {  
          var content = File.ReadAllText(this.Path);  
          ...  
     }  
    
    We have a dependency here making testing somewhat difficult. Whereas in some situations it's good pratice to wrap call to static methods into an instance, here we can get along without it:
     public void Init()  
     {  
          this.Init(File.ReadAllText);  
     }  
     internal void Init(ReadAllText readAllText)  
     {  
          var content = readAllText(this.Path);  
          ...  
     }  
     internal delegate string ReadAllText(string path);  

    Dependencies to the surface

    Somewhat related to the last topic on delegates is the the following: Often we programm quickly and only afterwards discover that we have dependencies, especially to built-ins like System.IO.FileInfo. Now, built-ins can have bugs, too. And a dependency on them should better be injected, e.g. for testability or for extendability. We can avoid missing those dependencies by not using global imports, by deleting any using statement at the beginning of our files and using full namespaces. What is the difference between
     using System.IO;  
     using System.Xml;  
     ...  
     internal void Init()  
     {  
          var file = new FileInfo(this.Path);  
          var dir = new DirectoryInfo(this.DirPath);  
          var xdoc = new XmlDocument();  
          ...  
     }  
    
    and
     internal void Init()  
     {  
          var file = new System.IO.FileInfo(this.Path);  
          var dir = new System.IO.DirectoryInfo(this.DirPath);  
          var xdoc = new System.Xml.XmlDocument();  
          ...  
     }  
    
    The difference is that in the second case you're getting so tired of typing the namespaces that you will want to do something about it. For the moment you cannot use global imports, so you will need to refactor to dependency injection, interface extraction etc.

    The protected antipattern

    There is this rule that production code and test code should not be mixed. In .NET we choose to create seperate test projects and only test against the public interface, letting private members become internal to be testable if necessary and sensible to do so.
    Another possibility is to set protected instead. Then in our test project we just inherit and overwrite.
    In my experience though, this second approach has at least two downsides that the use of internal doesn't have:

    • the access modifiers totally loose their sense because we cannot control what's done with protected members.
    • the inherited class will be tested and in a more complicated setup in the end we might loose track. Did we really test our code or the test code?

    Any vs. Some

    In order for our test cases to serve as documentation we need method and variable names communicating intention:
     public void TestCaseConstructorSetsProperties()  
     {  
          var to = new TestObject(anyParameter());  
          AssertThatPropertiesAreSet(to);  
     }  
    
    My personal gusto is only to use the prefix any if null is permitted, too. So if - following common practice - the constructor checks for null argument, this will be another test case. We can opt for using anyNonNullParameter() or simply:
     public void TestCaseConstructorSetsProperties()  
     {  
          var to = new TestObject(someParameter());  
          AssertThatPropertiesAreSet(to);  
     }  
    

    Advanced setup and teardown - cleanup files example

    Have a look at the following code:
     public void TestCaseUsingFileSystem()  
     {  
          var file = createTestFile();  
          var to = new TestObject();  
          to.doSomething(file);  
          AssertThatSomethingHoldsOn(to);  
          file.Delete();  
     }  
    
    The problem here is that the file probably won't be deleted if the assertion fails, making this test case fragile. Surely, you can think of other objects that might need proper tear down even if the test fails in order to assure correctness of the test fixture. A common solution of this problem is to introduce some class variable serving as trash and using a shared teardown. Mind also the file creation method which simply could have been called createTestFile() as before:
     public void TestCaseUsingFileSystem()  
     {  
          var file = createAndRegisterForCleanupTestFile();  
          var to = new TestObject();  
          to.doSomething(file);  
          AssertThatSomethingHoldsOn(to);  
     }  
     public void TearDown()  
     {  
          foreach(var item in this.trash)  
          {  
               try  
               {  
                    var file = item as File;  
                    if(file != null) file.Delete();  
                    ...  
               }catch(Exception e){  
                    reportToTestRunner(e.Message);  
               }  
          }  
     }  
     private File createAndRegisterForCleanupTestFile()  
     {  
          var file = createTestFile();  
          this.trash.Add(file);  
          return file;  
     }       

    Event checking

    You should always check if events are raised, too! An easy pattern for doing so is this:
     public void TestCaseSomeMethodRaisesEvent()  
     {  
          var eventHasBeenRaised = false;  
          testObject.SomeEventHandler = (sender, args) => eventHasBeenRaised = true;  
          testObject.SomeMethod();  
          AssertThat(eventHasBeenRaised);  
     }