1

We're in a process of writing Test framework (for Integration Testings) and I have a question regarding asserts. The test code looks like that:

public class MyTestClass 
{
    [Fact]
    public void MyTest() 
    {
        CreateEntity();
        DeleteEntity();
    }
}

The framework code looks like that:

public class CRUD 
{
    public bool CreateEntity() 
    {
        FuncA();
        FuncB();
        FuncC();
        FuncD();
    }
}

If FuncA() fails I don't want to run FuncB(), FuncC() or FuncD(). What I usually would do, is to check if FuncA() executed successfully using if-else sentence, and if not - I would have return false. My problem is that the person who write the Test-Code have to manually check if CreateEntity() return true or false. Because it's a test code, if CreateEntity() fails, there is no real meaning to continue the Test-run. I don't want the user to if-else every method on his Test-Case and assert on failure. Can I do the assert myself in the Framework method? for example -

public class CRUD 
{
    public bool CreateEntity() 
    {
        if (FuncA() == false) 
        {
            Assert.True(false, "FuncA() has failed");
        }

        FuncB();
        FuncC();
        FuncD();
    }
}

Is that considered a good idea? or the user should anyway do the inquiring himself? I want the creation of Test-Cases to be easy as possible to the user.

Thanks!

EDIT: Important thing to remember is this is an Integration Test Framework. Unlike Unit-Tests, we are performing various actions and try to see how they all work together. FuncA() go to the server and try to perform an action. This action should not normally fail. If it has some major basic functionality bug, it will be caught on our Unit-Test run. When it doesn't work in our Integration run - It might point to a bug that happens after specific set of previously made actions. So we want to know about it, and stop the test as the next actions will probably not work as they are integrated one into the other. So I'm not sure we want to mock "CreateEntity", as it gives us information about illness in the system. I do agree with you that Assert is not the right way to handle it, I'm just not completely sure exception is the right way to go, as I don't want to handle the problem, I want to report it and stop the test. I'm not too fond of the other option that requires the user to if/else every framework call and check for the results (which will create a messy code in my own opinion).

Okiba
  • 219
  • 3
  • 13
  • How are you exiting a function if it fails? Throwing an exception would allow you to avoid calling any further code. – rhughes Jun 22 '14 at 16:21

2 Answers2

0

Surround the functions in try catch. Then either throw the exception or have some logic in the catch to update the object with some info about the failure. I'd suggest throwing and make the object unaware. It shouldn't be in charge is handling it's own issues

Adrian Booth
  • 154
  • 1
  • 6
0

This really depends on your requirements for the CRUD class. As a general rule, I would echo what rhughes suggested in his comment--you should explicitly throw exception. The simplest sensible rewrite of your CreateEntity() method in this instance would be as follows:

public class CRUD 
{
    public bool CreateEntity() 
    {
        if (FuncA() == false) 
        {
            throw new Exception("FuncA() has failed");
        }

        FuncB();
        FuncC();
        FuncD();
    }
}

Alternatively, FuncA() itself could throw the exception if it fails, and you don't think it should be able to fail under normal execution. In either case, these will cause your test case to fail and the code in CreateEntity() to cease executing at the point that it is thrown.

I would argue against using test-framework asserts in application code--if you only add them to be consumed by tests then your application itself shouldn't know anything about them. Frameworks like MSTest use exceptions in methods like Assert.IsTrue anyway, so you'd end up with the same behaviour as manually throwing exceptions in your application code, but it would end up less readable and less easy to understand to the next person working on it.

EDIT: Have you considered abstracting the ugliness behind a method along the lines of:

private void WithEntity(Action action)
{
    if (!CreateEntity())
    {
        throw new Exception("Failed to create entity");
    }

    action();

    DeleteEntity();
}

You could then write your tests:

[Fact]
public void TestMethod1()
{
    WithEntity(() =>
    {
        // test code goes here
    });
}

It would hide some of the gnarliness. That way your application code can still return false; should FuncA(); fail, and you don't have to write tons of boilerplate each time you want to write a new test. You could even just add a [Setup] attribute to your integration test framework to run before each test / suite that looks something like:

[Setup]
public void Setup()
{
    if (!CreateEntity())
    {
        throw new Exception("Failed to create entity");
    }
}

...and a similar [Cleanup] attribute to handle run code after each test / suite (depending on which you need). Would either of those suggestions help?

EDIT 2: Another thing that could help here is the decorator pattern. If your CRUD class implements an interface, you could do something of the sort...

public class Crud : ICrud
{
    public bool CreateEntity()
    {
        // exciting business logic
    }

    // etc.
}

public class CrudTestDecorator : ICrud
{
    private readonly ICrud m_Crud;

    public CrudTestDecorator(ICrud crud)
    {
        m_Crud = crud;
    }

    public bool CreateEntity()
    {
        if (!m_Crud.CreateEntity())
        {
            throw new Exception("Could not create entity");
        }
    }

    // etc.
}

This fulfils more of your requirements:

  • Your business logic (in the main application) can still return bools
  • You can use your decorated object in any place you could use the original
  • You only have to write the exception-throwing code once
  • Your tests can fail more quickly
mpd106
  • 768
  • 4
  • 9
  • 1
    Thank you Matt D. I didn't want to use Exceptions because it felt "wrong". Like I'm tyring ti manipulate the flow of the method by exceptions. As far as I know, Exceptions should be a way to mark something that has really gone bad, and I didn't want to use it as an if-else alternative. Also, I wasn't able to find any information that supports using Exceptions as a method of logical handling your method. But your right that Asserts acts pretty much like Exceptions. – Okiba Jun 22 '14 at 18:18
  • I understand--if `FuncA()` can fail as part of the natural flow of your application, and this isn't something you'd **ever** want to handle at runtime in application code then exceptions might not be the answer for you. Having said that, I'd still not recommend using test-related assertions in your application code. A test that can sometimes fail is not a good test, which makes me feel uncomfortable about what you're trying to do with your `CreateEntity()` method. Is there a way you can use a mock or some other further abstraction to ensure that `CreateEntity()` is always successful? – mpd106 Jun 22 '14 at 23:15
  • Thanks again Matt. I tried to be more specific and answer your question, but sadly there is a limit to the information I'm able to post in a "Comment". I add what I wanted to the original post (under "EDIT") for your reviewing. Thanks! – Okiba Jun 23 '14 at 08:22
  • Sadly most of our Test code is much more complex. We do have [Setup] and [Cleanup] attributes for reoccurring code, but the gnarliness I talked about was on the actual Test-specific code. The user will have to "If (MyTestMethod1 == False) { Assert.False }, If (MyTestMethod2 = False) { Assert.False }" etc. I wanted to avoid the user manually checking each framework Method true/false condition. I think I now have a clearer picture: best practices suggest the user should check if the method succeeded, but test will be a bit messy. More user-friendly approach will to use exceptions not-so-properly – Okiba Jun 23 '14 at 22:43
  • Ah that makes sense. How about the decorator pattern? You've probably already thought about it, but I'll throw it out there--see edit 2 :) – mpd106 Jun 24 '14 at 08:07
  • Decorator pattern can indeed hide some of the clutter on some of our bigger methods and it's something will consider. I think that based on your comments, I will go with the philosophy of throwing exception on the business layer, and won't force the user to manually check the bool return code. Thank you very much mdp106, I really appreciate you help :) – Okiba Jun 24 '14 at 08:35