Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for NExpect, Level 1: Simple Stuff!
Davyd McColl
Davyd McColl

Posted on • Edited on

     

NExpect, Level 1: Simple Stuff!

Irecently introducedNExpect as an alternative assertions library. I thought it might be nice to go through usage, from zero to hero.

NExpect is available for .NET Framework 4.5.2 and above as well as
anything which can target .NET Standard 1.6 (tested with .NET Core 2.0)

So here goes, level 1: testing objects and values.

NExpect facilitates assertions (or, as I like to call them: expectations) against basic value types in a fairly unsurprising way:

[Test]publicvoidSimplePositiveExpectations{// Arrangeobjectobj=null;varintValue=1;vartrueValue=true;varfalseValue=false;// AssertExpect(obj).To.Be.Null();Expect(intValue).To.Equal(1);Expect(trueValue).To.Be.True();Expect(falseValue).To.Be.False();}
Enter fullscreen modeExit fullscreen mode

So far, nothing too exciting or unexpected there. NExpect also caters for negative expectations:

[Test]publicvoidSimpleNegativeExpectations{// Arrangeobjectobj=newobject();varintValue=1;vartrueValue=true;varfalseValue=false;// AssertExpect(obj).Not.To.Be.Null();Expect(intValue).Not.To.Equal(2);Expect(trueValue).Not.To.Be.False();Expect(falseValue).Not.To.Be.True();Expect(intValue).To.Be.Greater.Than(0);Expect(intValue).To.Be.Less.Than(10);}
Enter fullscreen modeExit fullscreen mode

(Though, in the above, I'm sure we all agree that the boolean expectations are neater without the .Not).

Expectations carry type forward, so you won't be able to, for example:

[Test]publicvoidExpectationsCarryType{Expect(1).To.Equal("a");// does not compile!}
Enter fullscreen modeExit fullscreen mode

However, expectations around numeric values perform upcasts in much the same way that you'd expect in live code, such that you can:

[Test]publicvoidShouldUpcastAsRequired(){// Arrangeinta=1;byteb=2;uintc=3;longd=4;// AssertExpect(b).To.Be.Greater.Than(a);Expect(c).To.Be.Greater.Than(b);Expect(d).To.Be.Greater.Than(a);}
Enter fullscreen modeExit fullscreen mode

All good and well, but often we need to check that a more complex object has a bunch of expected properties.

.Equal is obviously going to do reference-equality testing forclass types and value equality testing forstruct types. We could:

[Test]publicvoidTestingPropertiesOneByOne(){// Arrangevarperson=new{Id=1,Name="Jane",Alive=true};// AssertExpect(person.Id).To.Equal(1);Expect(person.Name).To.Equal("Jane");Expect(person.Alive).To.Be.True();}
Enter fullscreen modeExit fullscreen mode

But that kind of test, whilst perfectly accurate, comes at a cognitive overhead for the reader. Ok, perhaps not much overhead in this
example, but imagine if thatperson had come from another method:

[Test]publicvoidTestingPropertiesOneByOne(){// Arrangevarsut=newPersonRepository();// Actvarperson=sut.FindById(1);// AssertExpect(person).Not.To.Be.Null();Expect(person.Id).To.Equal(1);Expect(person.Name).To.Equal("Jane");Expect(person.Alive).To.Be.True();}
Enter fullscreen modeExit fullscreen mode

In this case, we'd expect the result to also have a defined type, not some anonymous type. It would be super-convenient if we could do deep equality testing. Which we can:

[Test]publicvoidDeepEqualityTesting(){// Arrangevarsut=newPersonRepository();// Actvarperson=sut.FindById(1);// AssertExpect(person).To.Deep.Equal(new{Id=1,Name="Jane",Alive=1});}
Enter fullscreen modeExit fullscreen mode

This exposes our test for what it's really doing: when searching for the person with the Id of 1, we should get back an object
which describes Jane in our system. Our test is speaking aboutintent, not just confirming value equality. Notice that the type
of the object used for comparison doesn't matter, and this holds for properties too.

Note that I omitted the test for null in the second variant. You
don't need it because the deep equality tester will deal with that
just fine. However, you are obviously still free to include it for the
sake of clarity.

NExpect gets this "for free" by depending on a git submodule ofPeanutButter and importing only the bits it needs. In this way, I can re-use
well-tested code and consumers don't have to depend on another Nuget package. Seems like a win to me.

What if we didn't care about all of the properties? What if we only cared about, for example,Name andId. A dead Jane is still a Jane, right?

NExpect has you covered:

[Test]publicvoidIntersectionEqualityTesting(){// Arrangevarsut=newPersonRepository();// Actvarperson=sut.FindById(1);// AssertExpect(person).To.Intersection.Equal(new{Id=1,Name="Jane"});}
Enter fullscreen modeExit fullscreen mode

We can also test the type of a returned object:

[Test]publicvoidTypeTesting(){// Arrangevarsut=newPersonRepository();// Actvarperson=sut.FindById(1);// AssertExpect(person).To.Be.An.Instance.Of<Person>();Expect(person).To.Be.An.Instance.Of<IPerson>();Expect(person).To.Be.An.Instance.Of<BaseEntity>();}
Enter fullscreen modeExit fullscreen mode

We can test for the exact type (Person), implemented interfaces (IPerson) and base types (BaseEntity).

We can also apply similar logic to testing exception throwing:

[TestFixture]publicclassTestingExceptions{[Test]publicvoidAssertingAgainstThrownExceptions(){Expect(()=>{}).Not.To.Throw();Expect(()=>Chucky()).To.Throw();// can drop the lambda though, for brevity:Expect(Chucky).To.Throw();// let's get more specific:Expect(Chucky).To.Throw<ArgumentException>();// more specific:Expect(Chucky).To.Throw<ArgumentException>().With.Message.Containing("cow says");// orExpect(Chucky).To.Throw<ArgumentException>().With.Property(e=>e.ParamName).Equal.To("moo");}privatevoidChucky(){thrownewArgumentException("The cow says moo","moo",newException("inner exception"));}}
Enter fullscreen modeExit fullscreen mode

Next, we'll explore simple collection testing.Tune in for Level 2!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Code monkey extraordinaire
  • Location
    Durban
  • Work
    Full stack dev at Codeo
  • Joined

More fromDavyd McColl

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp