데이터베이스 호출에 대한 단위 테스트를 작성하는 방법
나는 새 프로젝트의 시작 부분에 가까워졌고, 처음으로 내 프로젝트에 단위 테스트를 포함 시키려고 노력하고 있습니다.
일부 단위 테스트 자체를 고안하는 데 문제가 있습니다. 테스트하기에 충분히 쉬운 몇 가지 방법이 있습니다 (두 값을 전달하고 예상 출력을 확인). 데이터베이스에 대해 쿼리를 실행하는 것과 같이 더 복잡한 작업을 수행하는 코드의 다른 부분이 있으며 테스트 방법을 모르겠습니다.
public DataTable ExecuteQuery(SqlConnection ActiveConnection, string Query, SqlParameterCollection Parameters)
{
DataTable resultSet = new DataTable();
SqlCommand queryCommand = new SqlCommand();
try
{
queryCommand.Connection = ActiveConnection;
queryCommand.CommandText = Query;
if (Parameters != null)
{
foreach (SqlParameter param in Parameters)
{
queryCommand.Parameters.Add(param);
}
}
SqlDataAdapter queryDA = new SqlDataAdapter(queryCommand);
queryDA.Fill(resultSet);
}
catch (Exception ex)
{
//TODO: Improve error handling
Console.WriteLine(ex.Message);
}
return resultSet;
}
이 메서드는 기본적으로 데이터베이스에서 일부 데이터를 추출하는 데 필요한 모든 비트와 조각을 가져와 DataTable 개체에 데이터를 반환합니다.
첫 번째 질문은 아마도 가장 복잡 할 것입니다. 이런 상황에서 무엇을 테스트해야합니까?
일단 그것이 해결되면 데이터베이스 구성 요소를 모의하거나 실제 DB에 대해 테스트 할 것인지 여부에 대한 질문이 나옵니다.
무엇을 테스트하고 있습니까?
내 머리 위에 세 가지 가능성이 있습니다.
A. DAO (데이터 액세스 개체) 클래스를 테스트하여 데이터베이스로 전달되는 값 / 매개 변수를 올바르게 마샬링하고 데이터베이스에서 결과를 올바르게 마샬링 / 변환 / 패키징하고 있는지 확인합니다.
이 경우 데이터베이스에 연결할 필요가 전혀 없습니다. 데이터베이스 (또는 중간 계층, 예를 들어 JDBC, (N) Hibernate, iBatis)를 mock으로 대체하는 단위 테스트가 필요합니다.
B. (생성 된) SQL의 구문 정확성을 테스트하고 있습니다.
이 경우 SQL 언어가 다르기 때문에 RDBMS의 모든 단점을 모의하려고 시도하는 대신 RDBMS의 올바른 버전에 대해 (생성 된) SQL을 실행하려고합니다 (그리고 기능을 변경하는 모든 RDBMS 업그레이드가 귀하의 테스트).
C. SQL 의 의미 적 정확성, 즉 주어진 기준 데이터 세트에 대해 작업 (액세스 / 선택 및 변형 / 삽입 및 업데이트)이 예상되는 새 데이터 세트를 생성하는지 테스트 하고 있습니다.
:이를 위해, 당신은, 또는 아마도 당신 여기에 기술 I 개요를 사용하여 데이터베이스에 전적으로 테스트 할 (당신이 기준을 설정하고 예상되는 결과 세트에 대한 결과 세트를 비교할 수 있습니다) DBUnit를 같은 것을 사용하고자하는 가장 좋은 방법을 SQL 쿼리를 테스트합니다 .
이것이 (IMHO) 단위 테스트가 때때로 개발자 측에서 잘못된 보안 감각을 만들 수있는 이유입니다. 데이터베이스와 통신하는 애플리케이션에 대한 경험상 오류는 일반적으로 데이터가 예상치 못한 상태 (비정상적이거나 누락 된 값 등)에있는 결과입니다. 단위 테스트에서 일상적으로 데이터 액세스를 모의하면 코드가 실제로 이러한 종류의 오류에 취약 할 때 잘 작동한다고 생각할 것입니다.
최선의 접근 방식은 테스트 데이터베이스를 편리하고 엉뚱한 데이터로 가득 찬 다음 데이터베이스 구성 요소 테스트를 실행하는 것입니다. 사용자가 데이터를 망치는 것보다 훨씬 낫다는 것을 기억하십시오.
단위 테스트의 요점은 분리 된 상태에서 단위 (duh) 를 테스트하는 것 입니다. 데이터베이스 호출의 요점은 다른 유닛 (데이터베이스)과 통합 하는 것입니다. Ergo : 단위 테스트 데이터베이스 호출은 의미가 없습니다.
그러나 통합 테스트 데이터베이스 호출을 수행해야합니다 (원하는 경우 단위 테스트에 사용하는 것과 동일한 도구를 사용할 수 있음).
하나님의 사랑을 위해 이미 채워진 라이브 데이터베이스에 대해 테스트하지 마십시오. 그러나 당신은 그것을 알고있었습니다.
In general you already have an idea of what sort of data each query is going to retrieve, whether you're authenticating users, looking up phonebook/org chart entries, or whatever. You know what fields you're interested in, and you know what constraints exist on them (e.g., UNIQUE
, NOT NULL
, and so on). You're unit testing your code that interacts with the database, not the database itself, so think in terms of how to test those functions. If it's possible for a field to be NULL
, you should have a test that makes sure that your code handles NULL
values correctly. If one of your fields is a string (CHAR
, VARCHAR
, TEXT
, &c), test to be sure you're handling escaped characters correctly.
Assume that users will attempt to put anything* into the database, and generate test cases accordingly. You'll want to use mock objects for this.
* Including undesirable, malicious or invalid input.
Strictly speaking, a test that writes/reads from a database or a file system is not a unit test. (Although it may be an integration test and it may be written using NUnit or JUnit). Unit-tests are supposed to test operations of a single class, isolating its dependencies. So, when you write unit-test for the interface and business-logic layers, you shouldn't need a database at all.
OK, but how do you unit-test the database access layer? I like the advice from this book: xUnit Test Patterns (the link points to the book's "Testing w/ DB" chapter. The keys are:
- use round-trip tests
- don't write too many tests in your data access test fixture, because they will run much slower than your "real" unit tests
- if you can avoid testing with a real database, test without a database
You can unit test everything except: queryDA.Fill(resultSet);
As soon as you execute queryDA.Fill(resultSet)
, you either have to mock/fake the database, or you are doing integration testing.
I for one, don't see integration testing as being bad, it's just that it'll catch a different sort of bug, has different odds of false negatives and false positives, isn't likely to be done very often because it is so slow.
If I was unit testing this code, I'd be validating that the parameters are build correctly, does the command builder create the right number of parameters? Do they all have a value? Do nulls, empty strings and DbNull get handled correctly?
Actually filling the dataset is testing your database, which is a flaky component out of the scope of your DAL.
For unit tests I usually mock or fake the database. Then use your mock or fake implementation via dependency injection to test your method. You'd also probably have some integration tests that will test constraints, foreign key relationships, etc. in your database.
As to what you would test, you'd make sure that the method is using the connection from the parameters, that the query string is assigned to the command, and that your result set returned is the same as that you are providing via an expectation on the Fill method. Note -- it's probably easier to test a Get method that returns a value than a Fill method the modifies a parameter.
In order to do this properly though you would should use some dependency injection (DI), and for .NET there are several. I am currently using the Unity Framework but there are others that are easier.
Here is one link from this site on this subject, but there are others: Dependency Injection in .NET with examples?
This would enable you to more easily mock out other parts of your application, by just having a mock class implement the interface, so you can control how it will respond. But, this also means designing to an interface.
Since you asked about best practices this would be one, IMO.
Then, not going to the db unless you need to, as suggested is another.
If you need to test certain behaviors, such as foreign key relationships with cascade delete then you may want to write database tests for that, but generally not going to a real database is best, esp since more than one person may run a unit test at a time and if they are going to the same database tests may fail as the expected data may change.
Edit: By database unit test I mean this, as it is designed to just use t-sql to do some setup, test and teardown. http://msdn.microsoft.com/en-us/library/aa833233%28VS.80%29.aspx
On JDBC based project, JDBC connection can be mocked, so that tests can be executed without live RDBMS, with each test case isolated (no data conflict).
It allow to verify, persistence code passes proper queries/parameters (e.g. https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/ParameterSpec.scala) and handle JDBC results (parsing/mapping) as expected ("takes in all the necessary bits and pieces to extract some data from the database, and returns the data in a DataTable object").
Framework like jOOQ or my framework Acolyte can be used for: https://github.com/cchantep/acolyte .
참고URL : https://stackoverflow.com/questions/1217736/how-to-write-unit-tests-for-database-calls
'Programing' 카테고리의 다른 글
dotnet cli와 새로운 vs2017 msbuild 간의 관계 (0) | 2020.11.28 |
---|---|
ELMAH를 사용하는 WCF 서비스에 대한 예외 로깅 (0) | 2020.11.28 |
Scala 및 Haskell 유형 시스템의 차이점과 유사점은 무엇입니까? (0) | 2020.11.28 |
8 자 전용 UUID 생성 (0) | 2020.11.28 |
Jenkins (Hudson)에서 플러그인을 제거하려면 어떻게해야합니까? (0) | 2020.11.28 |