Testing Your Web Apps || Code Quality & Performance Virtual Conference
43K views
Nov 9, 2023
I will briefly go through the testing pyramid and tell about the unit, integration, automaton testing approaches for .NET web applications with some code examples. Conference Website: https://globaltechconferences.com/event/code-quality-performance-virtual-conference-2021/ C# Corner - Community of Software and Data Developers: https://www.c-sharpcorner.com C# Live - Dev Streaming Destination: https://csharp.live #Codequality #WebApps #Testing #VirtualConference
View Video Transcript
0:00
Let me start
0:03
So the topic is testing your web apps on ISP.net. So I am Vitaliy Honcharuk, and I am a full-stack software engineer
0:13
and I mostly work with C-sharp.net, Angular, SQL, and Azure. I have more than eight years of experience
0:21
And, okay, the goals for today's topic. So we are going to get acquainted with the testing pyramid, and we are going to understand different software testing types, software testing methodologies, and get acquainted with BDD, so in case somebody didn't hear about it
0:44
So if to go in general about the third testing, so here is a testing pyramid
0:50
I hope you guys heard about this, and if not, so let's go through it
0:57
So it describes the different levels of testing that ideally should be present in your project
1:09
So manual testing should be less than 10% or even don't exist on your projects
1:22
And also sometimes in case it's like a big project, there should be automated GUI tests
1:31
And this ideally should be performed by Kato automation engineers. And if we speak about automated API tests, also we have like three levels of integration testing
1:52
And one of them is automated API tests, automated integration tests, and automated component tests
2:00
And usually all of them are done by software developers, and some of them, like API tests, can be done by QA automation engineers
2:12
And surely, like we probably in almost all projects, we have unit tests
2:20
And it's also usually done by software developers. and what I want to mention also that if to look at this pyramid here at the
2:34
bottom we can see that like that if we go down by this pyramid we see that
2:45
there are much more surface. So it's like a little, it's like more unit
2:57
for example, unit tests, we have much more than integration tests and integration tests like more complex
3:07
And it's, okay. If to look from other perspective here at the bottom
3:15
we see that test amount is on this axis and the fragility and coverage and
3:23
implement is on this side. So if to go a little bit deeper into it, like we can
3:37
understand that integration tests can cover much more components as at the same time as unit tests cover only some single components
3:50
And like many developers know that we can't create a lot of integration tests
4:05
without stable environments because integration tests always like depending on the environment
4:16
And this is why it requires a lot of effort, actually, to create it, to support
4:23
And when it uses, like, real database, it takes much more time
4:34
And, yeah, it's more fragile. So let's go probably to another slide and speak a little bit more about what I've noticed
4:52
So in small non-tech companies, usually I don't see a separate software testing role
5:00
So software developers test their own code. So I guess it's not applicable for building great software because software developers should not test their own code
5:13
but in like non-tech companies like managers may not fully understand
5:22
about necessity of having the separate role to even have more than one developer
5:30
more than one engineer to test each other coding and implementation and also
5:37
in many projects lack of unit integration and automation tests or they, like these projects, can even don't have tests
5:50
So this leads to manual testing and this has really long-term implications
6:00
Actually, let's go to this slide first. So if to see on this slide, like on this axis, we can see, okay, on the blue axis, sorry, on the blue line, we can see like manual effort for doing testing
6:26
But on the orange line, we have automated testing
6:39
And if you yze this, we can see that integration or automation testing, it requires much more effort at the beginning
6:49
That is why in most of the cases, teams decided not to start creating unit tests, integration tests, and automation tests
7:05
but after some time when a project grows a lot and while it's growing as a manual
7:16
testing effort become much like growth also and at some point cost of manual testing and regression will be equivalent or start being even higher than having automated and integration tests
7:38
And this brings in my mind the idea that many teams should estimate
7:55
should include to the estimate of project development the time to create environments
8:02
and all required for integration tests and automated tests in case it's really needed
8:12
So this is why this point will be never met because in case a team will spend like half of the sprint
8:25
or the whole sprint to create, to prepare everything, to have integration tests and automation tests
8:37
they will not spend time on manual testing a lot like this, like here
8:44
So, and if to see, so also in the, how to say, so if to dig into the cost of the bug, if you will write this like query to Google, you will find such table in the Wikipedia
9:10
And here you can see that it's like approximate estimation of the cost of the bug when it's found
9:24
So in case bug found on the requirement stage, this will cost 1x
9:32
And in case this will be found on the post-release phase, it will cost 10 to 100 times even more than it's found on the requirement stage. so and
9:51
really cost to fix will be really high in this post-release stage and
9:57
here like you can see and get some ideas when should be found
10:04
the bugs so definitely on the requirement stage and from my experience in many
10:12
projects there are many issues, many problems with the requirement stage. I think
10:22
a lot of bugs came from there. A lot of communication effort involves because
10:31
in case requirements are not clear, developers often start creating additional
10:42
groomings, additional meetings, communicate between each other and try to find the truth
10:49
Where is the real requirements? And actually, this is a pain for many projects
10:58
So let me read through what did you write in the comments
11:04
Maybe is there some questions? Thanks for welcoming me
11:24
So here is a question about usability testing. Actually, usability testing is more about QA speciality, but here I am speaking about
11:35
like more programming testing, like unit tests, integration tests, and a little bit more
11:41
So here is like a full, probably almost a full hierarchy of testing
11:46
And here we can find a lot of different testing methodologies
11:56
testing, how to say it? Okay, different testing ways. And we will not dig into it and continue to integration testing
12:08
So we have different ways of integration testing that I actually already mentioned
12:16
And if to speak about simple integration testing, it's usually done by developers
12:22
And so integration tests, it needs to test a bunch of code nodes and components
12:32
So, for example, business logic along with database calls and calls to other services in case it's actually needed
12:44
And this is like almost the same as unit test, but with some real implementation
12:52
So it can be tested partially as a system because always when you have, for example, a bunch of microservices that are dependent between each other, and it's probably easier to test one to two to three, for example, services and mock some dependencies to some other services
13:15
and if to speak about API testing, it can be done by developers
13:23
and by QA automation engineers and in this case, like actually testing API
13:33
this is required for testing API for expected behavior, for example, with REST API calls
13:40
and it may be implemented on C Sharp and in the same way as unit tests
13:46
or any other language can be used which actually has testing frameworks
13:53
like N units, like X units we have in C Sharp. And what also I need to mention
14:01
is that this probably will require to have a QA API. Actually, what does it mean
14:07
In different cases, for example, for creation scenarios, we may have, like, create an endpoint
14:17
but for, for example, for getting this data, we may not have this endpoint
14:27
And that should be probably created in separate QA APIs that will be only visible for, like, testing environments
14:40
for development environments, environments but hidden on production. And yeah, same probably will be required in case you
14:50
for example case when you have some special system notifications that should be created for a user once some for example event in the system appears And this case like cannot be easily replicated
15:09
And this will definitely require to create some test QA API to create such notifications, like, on demand for tests
15:25
So I'm reading through your commands. Yes, it's true about Selenium
15:41
Selenium is cool things. Yeah. And yeah, let me tell a little bit more about it
15:48
So automated UI testing, it's probably another way to do integration testing
15:55
And this can be done by developers and by QA automations. But usually, from my experience, it's usually done by QA automation engineers
16:06
Because it requires, again, to have a separate QA API to get to expose more data from the database to make assertions and so on
16:18
And this can be done... Yeah, this automation test can be done by Selenium or Tosca framework
16:31
Tosca is a pretty new one that is really awesome, but costs a lot
16:39
So, yeah, let's go to the next slide. And restrictions to the integration tests in comparing with unit tests, it's probably I already mentioned, it's take more effort to implement and support
16:56
And it's very strict to test in environments and like third party integrations
17:04
And also it's very strict to existing database version and existing data
17:09
So it's, like, not easy to have lots of integration tests. And if to, like, speak a little bit more about how we can do integration tests in C Sharp
17:24
and especially in X unit, there are some small tips that may be useful
17:31
So, like, from my experience, I found that sometimes for integration tests, we use in-memory database in case we use, like, entity framework
17:44
And this can be a little bit tricky because, like, with in-memory database, LinQ will not be translated to real SQL
17:55
And this may cause like a fail. It may really fail on the real database, for example, in production
18:07
In case on testing environments, you use like in-memory database. That's why like ideal way to eliminate this issue
18:23
issue you can do integration tests on the for example SQLite or in on any real
18:32
database so yeah and other like tip for actually X unit to have for example to
18:49
share the context between different tests you can use constructor and dispose it
19:00
for each test and if you want to share context for different tests in the same
19:09
class, you can use class fixture. And to share all the, like, to share context with, for example
19:19
database or other connections or other useful things, you can use collection fixture. And
19:27
this will be easy to use for several classes. Also, other way how to do integration testing in
19:37
C Sharp and .NET applications, you can use Microsoft ISP.NET Core MBC testing package
19:47
And this is a very useful thing when you want to run the server and replace
20:04
some dependencies. So let me go deeper into it. So this integration
20:11
such integration tests requires to have a test project that will contain
20:17
like all the tests and this will be an entry point for
20:22
running tests. Sorry. And this test project will have a reference to system
20:33
under tests. So in our case it may be like some web application
20:38
And this test project will create a web host for the system
20:44
and will use a test server and client to handle requests and response with this system
20:54
And this test runner will be used to execute the test and report to the test results
21:05
And actually, this package doing everything under the hood for this scenario
21:12
And it actually copies dependencies file to the folder. And this..
21:24
Sorry. So it will copy all the dependencies in the bin directory
21:34
then it will set the content root to the web application project root
21:42
So the static files and pages views will be found for the tests and will be executed
21:52
And this library provides a web application factory class that will be easily used to streamline
22:03
and bootstrapping the system web application with the server. And actually, I will try to show
22:12
in case I will have time for this. And yeah, let me just continue
22:19
Yeah, the coolest thing that I found from this library is that along with creating a test server and providing a client for it under the hood you can actually provide stubs for third services
22:35
and check your system, like, partially integrated to your environment. And, yeah, this is awesome
22:44
And if to speak about other way of integration testing, As already mentioned, we can do automated UI testing with Selenium
23:02
And this is end-to-end test tool, and it's usually a tester's tools
23:10
but it can be useful for software developers to understand how does it work
23:16
And if to speak briefly about it, Selenium has different languages support and C-sharp, that is awesome for us
23:32
And this, actually, Selenium uses a browser driver
23:44
And for example, in case we use Chrome, it just operates with this browser driver
23:52
and open a real browser. And then it makes, like, swipes and finds objects on the page, like components, HTML tags, so by CSS selectors and so on
24:11
And how to say, interact with the page like a real user
24:16
and it can wait while some parts uploading, while some Ajax calls doing
24:26
And so also we can put a flag that we want to see
24:32
or hide actually actual browser. And probably it will be faster to have multiple threads running with Chrome driver
24:48
And in this case, probably you will want to hide the real browser window
24:58
So let me read about, let me read what did you write. So..
25:05
Yeah, so Selenium project is definitely like a different project and it will not be in
25:21
the same like library in the same project that you have for integration test
25:29
So usually it's like a separate environment because for this you need to create a new
25:35
some abstractions that will handle your CSS selector to get items that you need
25:49
Why we need integration tests is an interesting question. Actually, we need it to be much closer to user scenarios
25:58
So integration tests, with integration tests, we can cover more components working together
26:09
And in this case, this will be like system will be tested more complex than like by single component
26:23
Okay. Yeah, somebody writes it. I love Mediator. Yeah, I love to
26:29
So let's continue. So behavior-driven development. Let's spend a little bit time on this
26:37
So if to speak about BDD, let's probably start from DDD. Actually, domain-driven design, it's like a business subject focused development for every piece of code
26:54
So when teams has like DDD in mind, they think and, okay, they create common language with all stakeholders, with QA team, and each system part has like more business-involved meaning
27:19
And if to speak about TDD, test-driven development, this is like tests first and logic second development
27:31
So API always, or like different code abstractions, covered with tests. And there are, it's like common things that probably all of you already know, but if to go a little bit, how to say, a little bit back, there are always a lot of methods in requirements
27:57
and the formulation of complex scenarios and support and understanding of the implementation details
28:06
for all involved parties. So BDD came to solve this problem and behavior-driven development
28:15
it's like when coding begins with the requirements written in Gherkin syntaxes
28:22
which translated into acceptance tests. which requires to develop a logic to satisfy them
28:31
So this, like, when requirements, actually requirements may be written by business stakeholders
28:42
and they can do this in some IDE. For example, like, actually, they can do this in Visual Studio Code
28:53
with some special plugin for Gherkin syntaxes, or they can use a visual studio or some other, I don't know, cucumber
29:05
or other ideas that can support Gherkin. And so once business stakeholders wrote
29:15
requirements in the syntaxes, they can, they probably should commit this to Git or any other, like
29:26
any other systems that you use to store your repository. And then these tests can be written by developers or QAs
29:38
So I will try to show in case I will have time
29:42
or I should be much quicker to, you know, to show you
29:46
So then these tests that are created by QAs or developers should be read
29:59
because they don't have a system that actually implements the logic, and that is why developers then should implement the logic that will satisfy such tests
30:16
As a result, AppLogic will fully be covered by requirements via integration tests
30:24
And if to see briefly how does it work, So here is GERG and syntaxes and here like a feature name
30:36
It's like, for example, user stories. It can be splitted on some scenarios
30:43
And here is a scenario that's like a particular test case that somebody starts a game
30:53
and when a maker starts a game, then make your ways for breaker to join
31:01
So it's very similar to arrange, act, and assert. So it's like a given
31:13
It's a first part. It's arrange and act and assert. Given its initial state, when its action takes place
31:23
and then we check an expected outcome. And here how it looks like
31:30
And this syntax has a lot of other features. So here is a small example of how we can provide more than one hard-coded value
31:43
And here can be a table, and there are lots of different other features
31:47
And here how we can see this in the, how we can work with this in the Visual Studio
32:02
Yeah, so here is a feature file and we have some Gherkin scenario
32:11
and then we go to definition and in case it doesn't exist, we can generate it
32:16
and then it will be generated like empty step. And this step will look like this
32:30
So it will have almost the same string here and this will mean that it provides a value
32:38
It can actually get the value from the table that you can create then after this rows
32:46
And then you need to implement the logic for this actual step
32:52
And here we have already instantiated calculator class instance in this class
33:01
And here what we actually like adding the first number. And after we implement all the steps in the following way, then surely we should implement the logic of the calculator because on when syntax, on when step we will execute actual calculator action
33:30
and as it should be implemented to have a green test. Yeah, so the main advantage is a single source of truth
33:42
because requirements will be inbuilt in the project repository and always act all since tests are generated on the requirements basis
33:55
So as we see here is like requirements written in the Gürgen syntaxes and here we have like
34:03
already tests. It's like step for a test and we will have a lot of steps and we will have tests and
34:11
And to satisfy this test, we should have correct logic. Yeah, let me read again
34:23
So, okay. I..
34:34
Yeah, so for frontend, yeah, actually, yeah, We can use different, how to say, frameworks on front-end site
34:48
like, I don't know, Protractor or some others that I saw here
34:56
So it's like front-end unit tests in frameworks, and probably you can build their integration tests with it
35:06
but I'm not an expert in this area, so I will not answer
35:13
Okay. So I hope BDD will come into your mind when you will start a new project
35:25
I hope it will help you. So if to speak about stress testing and load testing
35:31
it's a little bit different from integration tests. But it's actually the purpose for load tests is to help to understand how system behaves under the expected load
35:46
And stress tests, it's helpful for you to understand upper limit of system capacity using the load beyond the expectation maximum
35:57
So when, actually, from my experience, from probably seven projects, I heard only one project that was used a lot in stress tests
36:15
It's probably not a common thing to use, but it's pretty interesting
36:20
And you can find, actually, bottlenecks in very interesting places. And probably if you have projects with pretty high loads, you should definitely use load tests and stress tests
36:35
And with stress tests, you can find some bottlenecks, and after resolving them, you may probably change your cloud configuration to have less CPU usage, sorry, less power of your cloud instance, so you will pay less
37:05
Okay, I have about 10 minutes and let's continue. So here lots of tools that you can use for stress and load testing, but usually it's not a developer
37:21
Usually developers do not do this and some special people doing this kind of test So I have a pretty less time sorry a pretty small amount of time and let me go
37:44
quickly to the project. So here is a project that follows the clean architecture principles but
37:52
but this is not as a purpose of this speech. And I just want to show the way of testing with API with ispanet core MBC testing
38:15
So here we have a test and here how we can instantiate the client
38:20
when we use actually this library. Let me just show the test
38:28
and then I will go to the web application factory. So it looks very simple
38:37
And here is a client creation. And here we execute our API with just direct URL
38:48
and we just do, for example, get async. And here is the response and we can check
38:54
is it successful and status code and we can deserialize data and we can check how many items we have in the collection
39:06
So as I already mentioned in the slides, when we use this ISP
39:17
Net MVC testing library we can have a default web application factory
39:28
or we can have like our special application factory with our how to say
39:39
with overriding of default startup file So actually, here you can see that I'm using startup files that is in the actual web project
39:50
And under the hood, this test will create the application host and the client
40:01
and also will provide the ability to override any dependency, actually any service in your services
40:14
So here I am getting the service from the services, and I'm removing it, and then I'm adding again this application DB context
40:26
and saying that I'm going to use here SQLite instead of default SQL server
40:34
And here, like, I am creating a service provider and here is a scope
40:43
And then here I am creating, I'm getting, again, DB context and I'm doing migration here
40:52
Then I'm doing some of my special things like getting user manager and role manager and creating data there
41:02
And then I'm seeding some default data for the tests. So here how it can be overwritten
41:15
So also here is example how to, for example, overwrite or add some I don know dummy implementation of some notification service for example in case we don want to spam our I don know email provider
41:38
And yeah, so that's probably it for this test factory. And I am having probably four minutes
41:46
So here how we can do this like very general for lots of classes and reuse it
41:54
But here is example how we can redefine for some special tests, some dependencies
42:04
And we can do this directly in this test and this will actually work
42:12
So here is it. And yeah, that's cool because when you run this
42:20
oops, oops, stop, stop lagging. And so once I run this test
42:30
it will create a test server and it will provide me a client to actually execute it
42:44
Yeah. So let's continue to the... It's building. Let's continue to the next probably class
42:57
Okay. And here is example a little bit more complex. So here probably I'm missing authorization
43:05
but here is how we can see like to do item creation
43:11
For example, at first we create to-do list, then we create to-do item, and all of that we can do with direct post queries
43:23
And then we are getting some paginated data from our API with using just simple HTTP calls
43:36
And the last thing is, again, about specflow. so yeah tests are successful here is it and last minute so here is like a
43:49
features and I can press F12 and I'm going to the direct implementation of
43:55
this step and again I am going to some other step and other cool things that in
44:01
case we need to implement some new step sorry some new scenario we
44:10
just copying existing steps that already implemented and just change whatever we need to change
44:17
For example, we want to do here something else and, for example, we will check something else
44:27
And not implemented steps just shown with this color. and
44:37
okay I will not show how we generate it but it's very easy
44:42
and actual steps will look like this and then you will again will be able to run
44:50
your tests from specflow implementation that's probably it and so we we found
45:02
our goals and And that's it
#Programming
#Windows & .NET