 On today's Visual Studio Toolbox, we're going to continue looking at unit testing tools, and we're going to explain X-Unit. Hi, welcome to Visual Studio Toolbox. I'm your host, Robert Green. Joining me is Phil Jepixi. Hey, Phil. Hey, Robert. This is part two of our multi-part series on unit testing. It is. We covered an introduction to unit testing and test-driven development in the previous episode. Today, we're going to do a deep dive on X-Unit. Yeah, we want to make sure that after all this is Visual Studio Toolbox, that we cover the tools so that you aren't hindered by the tooling. Right. Easy for me to say. So let's talk about X-Unit. So what is X-Unit? Give us a little history. Where do you get it? How do you add it to your project? Sure. Then we'll talk about how to use it. So there's a couple ways to add it. The easiest way is go out to NuGet and search for X-Unit. And it is a testing framework. It is a testing framework. And then it also has a runner built into it. Here the X-Unit.runner.visualstudio package enables it to tie into the Visual Studio Runner. It also works with Resharper and many other libraries. If you go out to NuGet, you can get add-ins for TeamCity, for VSTS, for MSBuild, for everything else. So you can run it from a build process as well. So it works inside Visual Studio, it works in the TestExplore, does it work with live unit testing? It does work with live unit testing as well. And the other way, real quick, if I say add new project, this is how important it is to Microsoft. I can go .NET Core, XUnitTestProject.NET Core. So it sets it up for you. You'll just want to update the packages because the template of course is, as soon as the template ships, it's out of date. With all templates. So then you would just right click and say update NuGet packages. Okay. So XUnit is what I typically use. It was one of the first ones to be compatible with .NET Core. It's not to say that the other testing frameworks are bad, it's just the one I prefer. Sure. And unit works, MB unit works, MSTest works. I have some issues with how it works. Fair enough. And we're going to cover a couple of those here early on, the differences. So we saw a quick demo in the last episode of XUnit. Again, I want to go into great detail. There's some name changes that might throw you for a little bit of a loop. Name changes from. Maybe 30 seconds, from NUnit and MBUnit. Okay. So in all the. So if you were using NUnit and MBUnit, things are named differently in XUnit. If you are new to all of these. Then it's on name changes. Right. Because it's brand new. Exactly. All right. But in the prior frameworks, we would call a test, a test. Seems quite simple. And then a data driven or scenario driven test, a row test. Typically. It's just a fact now. That's the name. It is a fact that it's a fact. Yeah, they just changed the name to fact in theory. In theory is then a data driven test. So that's something that might take you 30 seconds to get used to if you're coming from existing framework of it's brand new. Hey, you know what? We are stating that this is a fact that this assertion is true. And then we have a theory that all of these assertions and scenarios will be true. Okay. So we can also ignore tests. Now this is considered an anti-pattern in the testing world to ignore unit tests. Because if you have a broken unit test, you should fix the code. Or fix the test itself. Because tests can go stale, need to get fixed. But you know what? Sometimes reality happens and shipping's a feature and your build's not working because this test is failing, you can't figure it out. Whatever the reason being, you can ignore it so it's no longer run. But then I would strongly recommend you do something like add a to do or conversely you can also add the hack. And then that will show up in your task list to go fix. So let's talk about theories because I think that's one of the most powerful parts of X-Unit is the ability to use the exact same code, the assertion, I'm sorry, the arrange, the assert, and the arrange, action, and assert. It's easy for me to say after talking all day. And have multiple scenarios tested without having to keep copying code. We saw an example of the inline data and that's where we're passing data directly from the inline data attribute into these parameters on the unit test method itself. That's pretty straightforward. But let's talk about some additional powerful things that we can do and we can say, hey there is a member that I have access to because it's either in the class or within the referential patterns where I can return data. And so this test data method is returning an I and U of object. Each of these objects then get mapped to the parameters of the test itself. Now this is important if you've got something that's more complicated than adding in inline data. For example, when I was working in insurance we had these actuary tables where we had all these parameters we were throwing in. I didn't want to sit there and type every single line of Excel in the samples and the acceptance criteria into inline data but I could write a method that would then read the Excel file and do a yield return to return those values and map it. And if the accountant added more data than I just swap out the Excel file I don't have to change my code. That's extremely powerful. And the third option is we can have a class that implements I and U of object and do it that way. To be honest, I haven't used this in real life but I just felt important to show our viewers that it's able. But the two main methods I use for simple tests where I have a few cases or a few parameters I use inline data and then it's easy enough to add another scenario by simply copying that and let's say this is 100 and negative three and this would equal 97. Right. And this for more complicated data. Okay. I'm sorry about test running. For most people you'll never have to change this. X unit and it's runners run their tests in parallel. It's performance improvement. You can't really find a single core machine these days anyway, right? So you might as well take advantage of that. The way it decides what's parallel on what serial is based on the class level. So any tests inside a single class will all run serial but tests across classes will run parallel. Now this is a problem for you and I can't think of a situation where it would be a problem but if it is a problem for you you can disable that very simply by creating what's called a test collection. And what you do is you attribute your two classes with the collection attribute and the string name. Unfortunately it is a magic string but any test classes or fixtures that share the same name of the collection attribute will combine all those tests to run them serial. Okay. The other big change between the prior frameworks and X unit is how we handle exceptions. In N unit there is an expected exception attribute that we could decorate the test with. And the problem with that, and this isn't allowed anymore but let's just pretend it was, right? I could do, if I could type expected exception right here and it's not in there anymore as we see this is something different. And then we would put an exception type in there. Well the problem with that is what would happen if you're a range through that exception. Never calls into the system under test. Sorry as I use my exception large coffee cup to try and calm the cough. Then the test would be marked as passing because the exception didn't get thrown. But we never actually exercised the system under test. So that pattern has been decided to be an anti-pattern. And I don't wanna say that the folks who wrote those original frameworks were wrong. It's just we've learned different ways of doing testing and we continuously improve and do better. So what we do now is we assert that an operation throws an exception. So I have the system under test now. This is just hard coded to throw an error. But if I wanna say calculator.add int.maxvalue comma null and the null should throw an exception then I can trap it here. And I know that it's getting thrown at the invocation of the system under test and not somewhere in the test itself. It returns the exception for us. So then we can start looking at what's the message. If it's an argument null exception then we can look at what argument was missing and we can do all kinds of assertions on that exception. There's another way to do it that's a little more fluent. This is the arrange act assert syntax more closely where we say we're going to record an exception. Honestly, they both did the exact same thing. Dealers choice, whichever one you want to use. But what's important to note is that we want to check for the exception at the invocation of the system under test. Right. All right, somebody might be thinking, well what about test setup and test tear down? So in the other frameworks we've got this ability to do a fixture setup that's gonna execute before any other execute once before in your test or a test setup where we want to execute something before every test. So instead of marking methods with the setup and tear down attributes as we used to do we now use the constructor dispose pattern. So if you make your test fixture, I disposable, do you get that? No. Okay. I'm busy. Robert's phone just rang. So I was, hey, we'll wait. You guys wait too, right? So I mark it as I disposable. The constructor for the test will run for the class will run before every test. This is a test setup, not a fixture setup. And then the dispose will run after every test. So for example, if you are creating something, I do a lot of testing around Entity Framework Core. I'm not testing Entity Framework Core but I'm testing my data access layer. Right. There are not technical unit tests. They're gonna integration tests, whatever. This is called tests. Right. And get away from the academic debate. I want to have a brand new context before every test. I don't want to have anything in change tracker. I don't want to have any of that baggage with me. So in my constructor, I create a new context. Am I dispose? I dispose of the context. So that will happen again before and after every test. We can also set something up as what's called a class fixture. And when we use a class fixture, what happens is we get the equivalent of the fixture setup and fixture turnout. I'm gonna run this code one time before all my tests are executed. I'm gonna run this code one time after all my tests are executed. Okay. It's not a generally accepted pattern among most people anymore, but it's there if you need it. The test setup and tear down is really what we're driving people to use more and more as opposed to having this entire set of tests depending on a certain setup and tear down. And the reason being is we want our tests to be atomic. We want every test to be individual, not dependent on other tests and things like that. And we tend to start adding things into the tests that make them less atomic if we have this whole fixture set up and tear down. The last thing I want to cover is capturing output. Now, if you're running in, for example, ASP on that core and you're using Kestrel, all the output is done to the command window. But typically we're writing our unit tests in a class library. And we don't want to go through a whole bunch of hurdle of adding and logging and things like that necessarily, but we want to have output in there. Well, that's actually very, very simple in that we can in our tests add in the I test output helper. And then when we say output.writeLine, it's going to output to all of the standard out, right, which by default is the console. So I can add additional. So if you're trying to figure out what's going on, there are times when you're debugging a test and it behaves differently than when you're running at full on. And you want to be able to do the old, you get your old school like me, right? When we're doing classic ASP and you, you know, alert made it here, alert made it here, alert made it here, right? So this allows you to do that if you're in a situation where straight up debugging isn't working for you. So that's pretty much the power of X unit. I know I glossed over some of the things, but the most important thing I want people to take away from this is it works in core, works in full dynamic framework. It's free. It works with pretty much any build server that you have. I haven't yet hit a situation where it's not supported by a build server. You get the road test capability or the scenario based testing capability. And we try not to call it data driven testing because then people think, boy, you're using a database. Yeah, even though it's inline data. So we call it scenario based. And then you can control the parallelism and then we trap exceptions at invocation as opposed to anywhere in there. Cool. So any questions about X unit? There's a ton more in X unit, but these are the features I think people are gonna use most often. Okay, so again, this is a test framework. So you've shown us the scaffolding, if you will. And then the tests you write are typically gonna be independent of the framework? To what extent, yeah, that's a good question. To what extent did the actual tests you write depend on the framework you're using? The dependency is mostly in the asserts. Okay. So for example, in X unit, it's assert.equal. And in unit, it was, if my memory serves, assert.isequal, right? Or are equal. I don't remember the exact, so some of the syntax. So this is very dependent. All these asserts come from X unit. They're built into it. So that's dependent on the framework, right? Some of the syntax things. But this code here, my arrange of my act, totally independent of the framework, right? We are dependent on the framework when we're looking at exceptions. So when we get into here, right? This syntax here on line 22 and this syntax on line 30, also dependent on the framework. Right, okay. But the actual invocation of your system under test, the creation of your mocks, which we're gonna cover, I believe in the next episode, that is all independent of X unit and unit MS test. Right, cool. So any other questions about X unit? Nope, all right. All right, so that was a great overview of X unit, testing framework. And in our next episode, we are going to cover. Machu. Machu, awesome, see you then. Thanks for watching.